嵌入式C++教程:placement new¶
在嵌入式世界里,new / delete 往往不是"万能钥匙"。有的目标平台根本没有自由堆(裸机、某些 RTOS),有的场景为了可预测性和实时性要禁用 heap,更有些情形下你需要把内存布局控制到厘米级——这就要靠 placement new(放置 new)来做事情了。
所以,这一次我们来讨论下placement new这个小东西。
所以什么是 placement new?怎么用¶
最简单的例子——把一个对象放进栈上的一个缓存区:
#include <new> // for placement new
#include <cstddef>
#include <iostream>
#include <type_traits>
struct Foo {
int x;
Foo(int v): x(v) { std::cout << "Foo(" << x << ") ctor\n"; }
~Foo() { std::cout << "Foo(" << x << ") dtor\n"; }
};
int main() {
// 为了安全,使用对齐良好的 unsigned char 缓冲区
alignas(Foo) unsigned char buffer[sizeof(Foo)];
// placement new:在 buffer 起始处构造 Foo
Foo* p = new (buffer) Foo(42); // 与 delete 无关
std::cout << "p->x = " << p->x << "\n";
// 显式析构(非常重要)
p->~Foo();
}
看到了这个new嘛?实际上这里的new(buffer) Foo(args...)只是调用构造函数,在 buffer 指定的位置构造对象,而且注意到,这个区域是实际放在栈上的,不能对 placement-new 的对象使用 delete p;;必须显式调用 p->~Foo()。当然,为了满足对齐,缓冲区应使用 alignas(T) 或 std::aligned_storage_t。
不过这只是一个示例用法,没有人真的会这样使用的。。。
对齐与内存布局——别让 UB 找上门¶
对齐问题在嵌入式里是头等大事。构造 Foo 前,缓冲区必须满足 alignof(Foo)。常见做法:
// C++11/14 风格(仍可用)
using Storage = typename std::aligned_storage<sizeof(Foo), alignof(Foo)>::type;
Storage storage;
Foo* p = new (&storage) Foo(1);
// C++17 以后更直观的方式
alignas(Foo) unsigned char storage2[sizeof(Foo)];
Foo* q = new (storage2) Foo(2);
如果你自己写 allocator,要实现 align_up(),把返回地址向上对齐到 align 的倍数(使用 uintptr_t 算术)。
异常安全与构造失败¶
当构造函数可能抛异常时,placement new 的异常处理要小心——如果构造失败,则不会产生要显式析构的对象(因为没有成功构造),但如果你在一段复杂的初始化里分多步构造多个对象,就要在 catch 中正确回滚已经成功构造的部分。
// 伪代码:在一段连续缓冲区中构造多个对象
Foo* objs[3];
unsigned char* buf = ...; // 足够大、对齐良好
unsigned char* cur = buf;
int constructed = 0;
try {
for (int i = 0; i < 3; ++i) {
void* slot = cur; // assume aligned
objs[i] = new (slot) Foo(i); // 可能抛
++constructed;
cur += sizeof(Foo); // 简化示意
}
} catch (...) {
// 回滚已经构造的对象
for (int i = 0; i < constructed; ++i) objs[i]->~Foo();
throw; // 继续抛出或记录错误
}
总之:构造失败会中断流程,但不会自动清理已构造对象,这事儿你要负责。
Bump(线性)分配器 + placement new¶
在嵌入式里最常见的替代方案是 arena / bump allocator:预先申请一块大内存,然后按需线性分配;析构通常在整个 arena reset 的时候统一做。它非常适合"启动时分配后长期存在"的对象,例如 drivers、初始化数据等。arena / bump allocator会之后专门聊,这里就是看看。
#include <cstdint>
#include <cstddef>
#include <cassert>
#include <new>
struct BumpAllocator {
uint8_t* base;
size_t capacity;
size_t offset;
BumpAllocator(void* mem, size_t cap) : base(static_cast<uint8_t*>(mem)), capacity(cap), offset(0) {}
// 返回已对齐的指针,或 nullptr(失败)
void* allocate(size_t n, size_t align) {
uintptr_t cur = reinterpret_cast<uintptr_t>(base + offset);
uintptr_t aligned = (cur + align - 1) & ~(align - 1);
size_t nextOffset = aligned - reinterpret_cast<uintptr_t>(base) + n;
if (nextOffset > capacity) return nullptr;
offset = nextOffset;
return reinterpret_cast<void*>(aligned);
}
void reset() { offset = 0; }
};
struct Bar {
int v;
Bar(int x): v(x) {}
~Bar() {}
};
int main() {
static uint8_t arena_mem[1024];
BumpAllocator arena(arena_mem, sizeof(arena_mem));
void* p = arena.allocate(sizeof(Bar), alignof(Bar));
Bar* b = nullptr;
if (p) b = new (p) Bar(100); // placement new
// 使用 b...
b->~Bar(); // 如果你需要提前析构单个对象(可选)
// 更常见:app 结束或 mode 切换时统一 reset:
// arena.reset(); // 这不会调用析构函数——只适用于 POD 或者你自己管理析构
}
注意:arena.reset() 不会自动调用析构函数——如果对象有重要资源(文件、mutex、heap),你必须先显式析构。
对象池(free-list)——支持释放/重用¶
还记得我们的对象池嘛?当你既要控制内存又要动态释放时,最常见模式是对象池(free-list)+ placement new。适合固定大小对象的高频分配/释放(例如网络包、任务结构体)。
#include <cstddef>
#include <new>
#include <cassert>
// 简化的对象池(单线程示例)
template<typename T, size_t N>
class ObjectPool {
union Slot {
Slot* next;
alignas(T) unsigned char storage[sizeof(T)];
};
Slot pool[N];
Slot* free_head = nullptr;
public:
ObjectPool() {
// 初始化 free list
for (size_t i = 0; i < N - 1; ++i) pool[i].next = &pool[i+1];
pool[N-1].next = nullptr;
free_head = &pool[0];
}
template<typename... Args>
T* allocate(Args&&... args) {
if (!free_head) return nullptr;
Slot* s = free_head;
free_head = s->next;
T* obj = new (s->storage) T(std::forward<Args>(args)...);
return obj;
}
void deallocate(T* obj) {
if (!obj) return;
obj->~T();
// 将 slot 重回 free list
Slot* s = reinterpret_cast<Slot*>(reinterpret_cast<unsigned char*>(obj) - offsetof(Slot, storage));
s->next = free_head;
free_head = s;
}
};
要点:
offsetof(Slot, storage)用来回算出 slot 起点(小心可移植性——这里是常见技巧);- 如果你需要多线程访问,记得给 pool 加锁或使用无锁结构。
为了不把指针玩残——std::launder 有什么用?¶
当你在同一内存位置反复 placement new 相同类型对象时,某些情况下需要 std::launder 来取得"有效"的指针,避免编译器优化引起的问题。简单示意(C++17 增):
#include <new>
#include <memory> // for std::launder
alignas(Foo) unsigned char buf[sizeof(Foo)];
Foo* a = new (buf) Foo(1);
a->~Foo();
Foo* b = new (buf) Foo(2);
// 如果你以前保存了旧指针 a,重新使用它可能是 UB。
// 使用 std::launder 可以得到新的、可靠的指针:
Foo* safe_b = std::launder(reinterpret_cast<Foo*>(buf));
通常在嵌入式代码中,直接把指针存放在 local 变量并谨慎管理生命周期就行;但当你面对别名 / 编译器优化带来的潜在的小bug,std::launder 可以派上用场。
把繁琐变得可用:写一个小型 InPlace RAII wrapper¶
重复写 placement + 显式析构容易出错,做个小封装能让代码更干净:
#include <new>
#include <type_traits>
#include <utility>
template<typename T>
class InPlace {
alignas(T) unsigned char storage[sizeof(T)];
bool constructed = false;
public:
InPlace() noexcept = default;
template<typename... Args>
void construct(Args&&... args) {
if (constructed) this->destroy();
new (storage) T(std::forward<Args>(args)...);
constructed = true;
}
void destroy() {
if (constructed) {
reinterpret_cast<T*>(storage)->~T();
constructed = false;
}
}
T* get() { return constructed ? reinterpret_cast<T*>(storage) : nullptr; }
~InPlace() { destroy(); }
};
有了 InPlace<T>,你可以把生命周期绑定到函数/对象上,防止忘记析构(RAII FTW)。
什么时候不要用 placement new?¶
- 你需要复杂的内存分配策略(碎片整理、回收策略)——更完善的 allocator(TLSF、slab、buddy)会更适合;
- 对象很大或构造很昂贵但频繁创建/销毁,除非有充分理由,用动态内存或成熟池更省心;
- 不能保证在异常或中断下正确析构资源的场景。
所以¶
在没有堆的世界里,placement new 就像一个"小而美"的工具箱:你把对象放在哪里,什么时候构造、什么时候拆掉,都由你说了算。这既带来巨大的可控性,也把一些原本由 runtime 负责的细节交还给你——你要负责任地管理生命周期、对齐与异常。
如果你是那种喜欢把内存"画成格子"的人,placement new 会让你很开心;如果你不想手动管理生命周期,那么你要么带上 RAII 护甲(自己写或用框架),要么就接受有点儿更大的运行时(受控的 heap)。总之,嵌入式没有完美解,只有合适的解——placement new 是一把锋利而可靠的小刀,用好了事半功倍,用不好就是割手指。
代码示例¶
查看完整可编译示例
#include <iostream>
#include <new>
#include <cstddef>
#include <cstdint>
#include <type_traits>
// 演示placement new的基础用法
struct Foo {
int x;
double y;
Foo(int v) : x(v), y(v * 1.5) {
std::cout << "Foo(" << x << ", " << y << ") constructed\n";
}
~Foo() {
std::cout << "Foo(" << x << ", " << y << ") destructed\n";
}
void print() const {
std::cout << "Foo{x=" << x << ", y=" << y << "}\n";
}
};
void basic_placement_new_demo() {
std::cout << "=== Basic Placement New Demo ===\n\n";
// 方法1: 使用 alignas 和 unsigned char 数组
std::cout << "--- Method 1: alignas + unsigned char array ---\n";
{
alignas(Foo) unsigned char buffer1[sizeof(Foo)];
std::cout << "Buffer address: " << static_cast<void*>(buffer1) << "\n";
std::cout << "Buffer aligned to " << alignof(Foo) << " bytes? "
<< (reinterpret_cast<uintptr_t>(buffer1) % alignof(Foo) == 0) << "\n";
Foo* p1 = new (buffer1) Foo(42);
p1->print();
// 显式析构(非常重要!)
p1->~Foo();
}
// 方法2: 使用 std::aligned_storage (C++11/14风格)
std::cout << "\n--- Method 2: std::aligned_storage (C++11/14) ---\n";
{
using Storage = typename std::aligned_storage<sizeof(Foo), alignof(Foo)>::type;
Storage storage2;
std::cout << "Storage address: " << &storage2 << "\n";
Foo* p2 = new (&storage2) Foo(100);
p2->print();
p2->~Foo();
}
// 方法3: C++17 std::aligned_storage_t (更简洁)
std::cout << "\n--- Method 3: std::aligned_storage_t (C++17) ---\n";
{
std::aligned_storage_t<sizeof(Foo), alignof(Foo)> storage3;
Foo* p3 = new (&storage3) Foo(200);
p3->print();
p3->~Foo();
}
// 多个对象的placement new
std::cout << "\n--- Multiple Objects in Array ---\n";
{
constexpr size_t N = 3;
alignas(Foo) unsigned char buffer[N * sizeof(Foo)];
Foo* objs[N];
for (size_t i = 0; i < N; ++i) {
void* slot = buffer + i * sizeof(Foo);
objs[i] = new (slot) Foo(static_cast<int>(i * 10));
}
for (size_t i = 0; i < N; ++i) {
objs[i]->print();
}
// 反向析构(构造的逆序)
for (int i = static_cast<int>(N - 1); i >= 0; --i) {
objs[i]->~Foo();
}
}
}
// 异常安全的placement new
void exception_safe_demo() {
std::cout << "\n=== Exception Safe Placement New ===\n\n";
struct MightThrow {
int value;
bool should_throw;
MightThrow(int v, bool throw_flag) : value(v), should_throw(throw_flag) {
if (should_throw) {
throw std::runtime_error("Construction failed");
}
std::cout << "MightThrow(" << value << ") constructed\n";
}
~MightThrow() {
std::cout << "MightThrow(" << value << ") destructed\n";
}
};
constexpr size_t N = 3;
alignas(MightThrow) unsigned char buffer[N * sizeof(MightThrow)];
MightThrow* objs[N] = {nullptr};
int constructed = 0;
try {
for (int i = 0; i < 3; ++i) {
void* slot = buffer + i * sizeof(MightThrow);
// 第三个对象会抛出异常
objs[i] = new (slot) MightThrow(i, (i == 2));
++constructed;
}
} catch (const std::exception& e) {
std::cout << "Exception caught: " << e.what() << "\n";
std::cout << "Constructed " << constructed << " objects before failure\n";
// 回滚已经构造的对象
for (int i = constructed - 1; i >= 0; --i) {
if (objs[i]) {
objs[i]->~MightThrow();
}
}
}
}
// 对齐检查工具
template<typename T>
bool is_aligned(void* ptr, size_t alignment = alignof(T)) {
return reinterpret_cast<uintptr_t>(ptr) % alignment == 0;
}
void alignment_check_demo() {
std::cout << "\n=== Alignment Check Demo ===\n\n";
// 检查不同类型的对齐要求
std::cout << "Alignment requirements:\n";
std::cout << " char: " << alignof(char) << " bytes\n";
std::cout << " int: " << alignof(int) << " bytes\n";
std::cout << " double: " << alignof(double) << " bytes\n";
std::cout << " int64_t: " << alignof(int64_t) << " bytes\n";
// 演示未对齐的后果
alignas(int) unsigned char buffer[32];
for (int offset = 0; offset < 4; ++offset) {
void* ptr = buffer + offset;
bool aligned = is_aligned<int>(ptr);
std::cout << " Offset " << offset << ": aligned? " << aligned << "\n";
if (aligned) {
int* p = new (ptr) int(42);
std::cout << " Successfully constructed int at " << p << "\n";
p->~int();
}
}
}
// 在栈上构造"动态"大小的对象
void stack_vector_demo() {
std::cout << "\n=== Stack-Allocated \"Dynamic\" Container ===\n\n";
template<size_t N>
class StackVector {
alignas(double) unsigned char buffer_[N * sizeof(double)];
size_t size_ = 0;
public:
void push(double v) {
if (size_ < N) {
new (buffer_ + size_ * sizeof(double)) double(v);
++size_;
}
}
double& operator[](size_t i) {
return *reinterpret_cast<double*>(buffer_ + i * sizeof(double));
}
size_t size() const { return size_; }
~StackVector() {
for (size_t i = 0; i < size_; ++i) {
reinterpret_cast<double*>(buffer_ + i * sizeof(double))->~double();
}
}
};
StackVector<10> vec;
for (int i = 0; i < 5; ++i) {
vec.push(i * 1.1);
}
std::cout << "StackVector contents: ";
for (size_t i = 0; i < vec.size(); ++i) {
std::cout << vec[i] << " ";
}
std::cout << "\n";
}
int main() {
basic_placement_new_demo();
exception_safe_demo();
alignment_check_demo();
stack_vector_demo();
std::cout << "\n=== Key Takeaways ===\n";
std::cout << "1. Placement new constructs objects at pre-allocated memory\n";
std::cout << "2. Always call destructor explicitly for placement-new objects\n";
std::cout << "3. Use alignas() or aligned_storage for proper alignment\n";
std::cout << "4. Exception safety requires manual cleanup in catch blocks\n";
std::cout << "5. Never use delete on placement-new objects\n";
return 0;
}
#include <iostream>
#include <cstdint>
#include <cstddef>
#include <cstring>
#include <new>
// Bump/线性分配器 + placement new
class BumpAllocator {
uint8_t* base_;
size_t capacity_;
size_t offset_;
public:
BumpAllocator(void* mem, size_t cap)
: base_(static_cast<uint8_t*>(mem)), capacity_(cap), offset_(0) {}
// 返回已对齐的指针,或 nullptr(失败)
void* allocate(size_t n, size_t align = alignof(std::max_align_t)) {
uintptr_t cur = reinterpret_cast<uintptr_t>(base_ + offset_);
uintptr_t aligned = (cur + align - 1) & ~(align - 1);
size_t aligned_offset = aligned - reinterpret_cast<uintptr_t>(base_);
if (aligned_offset + n > capacity_) {
return nullptr; // 溢出
}
offset_ = aligned_offset + n;
return reinterpret_cast<void*>(aligned);
}
void reset() {
offset_ = 0;
}
size_t used() const { return offset_; }
size_t available() const { return capacity_ - offset_; }
size_t capacity() const { return capacity_; }
// 获取当前分配位置(用于调试)
void* current_position() const {
return base_ + offset_;
}
};
struct Widget {
int id;
char name[32];
double value;
Widget(int i, const char* n, double v) : id(i), value(v) {
std::strncpy(name, n, sizeof(name) - 1);
name[sizeof(name) - 1] = '\0';
std::cout << "Widget " << id << " (" << name << ") constructed\n";
}
~Widget() {
std::cout << "Widget " << id << " (" << name << ") destructed\n";
}
void print() const {
std::cout << "Widget{id=" << id << ", name=" << name
<< ", value=" << value << "}\n";
}
};
void basic_bump_demo() {
std::cout << "=== Basic Bump Allocator Demo ===\n\n";
// 静态缓冲区作为arena
static uint8_t arena_mem[4096];
BumpAllocator arena(arena_mem, sizeof(arena_mem));
std::cout << "Arena: " << arena.capacity() << " bytes\n";
std::cout << "Initial available: " << arena.available() << " bytes\n\n";
// 分配对象
std::cout << "--- Allocating objects ---\n";
void* p1 = arena.allocate(sizeof(Widget), alignof(Widget));
if (p1) {
Widget* w1 = new (p1) Widget(1, "First", 3.14);
w1->print();
}
void* p2 = arena.allocate(sizeof(Widget), alignof(Widget));
if (p2) {
Widget* w2 = new (p2) Widget(2, "Second", 2.71);
w2->print();
}
std::cout << "\nUsed: " << arena.used() << " bytes\n";
std::cout << "Available: " << arena.available() << " bytes\n";
// 手动析构
std::cout << "\n--- Manual destruction ---\n";
if (p1) {
static_cast<Widget*>(p1)->~Widget();
}
if (p2) {
static_cast<Widget*>(p2)->~Widget();
}
// 重置arena
std::cout << "\n--- Resetting arena ---\n";
arena.reset();
std::cout << "After reset - Used: " << arena.used() << " bytes\n";
std::cout << "After reset - Available: " << arena.available() << " bytes\n";
}
// 混合大小分配
void mixed_size_demo() {
std::cout << "\n=== Mixed Size Allocation Demo ===\n\n";
static uint8_t arena_mem[2048];
BumpAllocator arena(arena_mem, sizeof(arena_mem));
// 分配不同大小的对象
struct Small { char data[16]; };
struct Medium { char data[64]; };
struct Large { char data[256]; };
void* p1 = arena.allocate(sizeof(Small), alignof(Small));
void* p2 = arena.allocate(sizeof(Medium), alignof(Medium));
void* p3 = arena.allocate(sizeof(Large), alignof(Large));
void* p4 = arena.allocate(sizeof(Small), alignof(Small));
std::cout << "Allocations:\n";
std::cout << " Small: " << p1 << "\n";
std::cout << " Medium: " << p2 << "\n";
std::cout << " Large: " << p3 << "\n";
std::cout << " Small: " << p4 << "\n";
std::cout << "\nUsed: " << arena.used() << " bytes\n";
// 尝试分配太大的对象
void* p5 = arena.allocate(4096, 8);
std::cout << "\nTrying to allocate 4096 bytes: " << (p5 ? "succeeded" : "failed (expected)") << "\n";
}
// Arena生命周期管理
class ScopedArena {
BumpAllocator& arena_;
size_t initial_offset_;
public:
explicit ScopedArena(BumpAllocator& arena)
: arena_(arena), initial_offset_(arena.used()) {}
~ScopedArena() {
// 析构时回退到初始位置
// 注意:这不会调用析构函数!
arena_.reset();
arena_ = BumpAllocator(
reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(&arena_) - initial_offset_),
arena_.capacity()
);
}
// 禁止拷贝和移动
ScopedArena(const ScopedArena&) = delete;
ScopedArena& operator=(const ScopedArena&) = delete;
};
void arena_region_demo() {
std::cout << "\n=== Arena Region Demo ===\n\n";
static uint8_t arena_mem[4096];
BumpAllocator arena(arena_mem, sizeof(arena_mem));
// 使用1:初始化阶段
std::cout << "--- Initialization Phase ---\n";
size_t init_start = arena.used();
void* p1 = arena.allocate(sizeof(int), alignof(int));
void* p2 = arena.allocate(sizeof(double), alignof(double));
new (p1) int(42);
new (p2) double(3.14);
std::cout << "Init used: " << (arena.used() - init_start) << " bytes\n";
// 使用2:运行时临时对象
std::cout << "\n--- Runtime Phase ---\n";
size_t runtime_start = arena.used();
// 临时分配
void* temp1 = arena.allocate(128, 8);
void* temp2 = arena.allocate(64, 8);
std::cout << "Runtime used: " << (arena.used() - runtime_start) << " bytes\n";
// 运行时结束,回退
std::cout << "\n--- Reset to init phase ---\n";
// 保存init对象,回退runtime分配
int* init_int = static_cast<int*>(p1);
double* init_double = static_cast<double*>(p2);
size_t init_used = arena.used() - runtime_start;
// 这不是正确的做法,需要保存init对象数据
// 实际应用中应该使用分层arena
}
// 分层Arena
class TieredArena {
static uint8_t memory_[8192];
BumpAllocator permanent_; // 永久对象
BumpAllocator frame_; // 帧临时对象
public:
TieredArena()
: permanent_(memory_, 4096)
, frame_(memory_ + 4096, 4096) {}
void* allocate_permanent(size_t n, size_t align = alignof(std::max_align_t)) {
return permanent_.allocate(n, align);
}
void* allocate_frame(size_t n, size_t align = alignof(std::max_align_t)) {
return frame_.allocate(n, align);
}
void reset_frame() {
frame_.reset();
}
size_t permanent_used() const { return permanent_.used(); }
size_t frame_used() const { return frame_.used(); }
};
uint8_t TieredArena::memory_[8192];
void tiered_arena_demo() {
std::cout << "\n=== Tiered Arena Demo ===\n\n";
TieredArena arena;
std::cout << "--- Permanent allocations ---\n";
void* p1 = arena.allocate_permanent(sizeof(int), alignof(int));
void* p2 = arena.allocate_permanent(sizeof(double), alignof(double));
new (p1) int(100);
new (p2) double(2.718);
std::cout << "Permanent used: " << arena.permanent_used() << " bytes\n";
std::cout << "\n--- Frame allocations ---\n";
void* f1 = arena.allocate_frame(128, 8);
void* f2 = arena.allocate_frame(256, 8);
std::cout << "Frame used: " << arena.frame_used() << " bytes\n";
std::cout << "\n--- Reset frame ---\n";
arena.reset_frame();
std::cout << "After reset - Frame used: " << arena.frame_used() << " bytes\n";
std::cout << "Permanent still: " << arena.permanent_used() << " bytes\n";
}
int main() {
basic_bump_demo();
mixed_size_demo();
arena_region_demo();
tiered_arena_demo();
std::cout << "\n=== Key Takeaways ===\n";
std::cout << "1. Bump allocator is extremely fast (pointer arithmetic)\n";
std::cout << "2. No fragmentation, but can't free individual objects\n";
std::cout << "3. Perfect for initialization or frame-based allocation\n";
std::cout << "4. reset() doesn't call destructors - manage manually\n";
std::cout << "5. Use tiered arenas for mixed lifetime requirements\n";
return 0;
}
#include <iostream>
#include <new>
#include <type_traits>
#include <utility>
#include <cstdint>
// InPlace RAII Wrapper - 简化placement new的使用
template<typename T>
class InPlace {
alignas(T) unsigned char storage_[sizeof(T)];
bool constructed_ = false;
public:
InPlace() noexcept = default;
// 析构时自动清理
~InPlace() {
destroy();
}
// 禁止拷贝
InPlace(const InPlace&) = delete;
InPlace& operator=(const InPlace&) = delete;
// 支持移动
InPlace(InPlace&& other) noexcept {
if (other.constructed_) {
new (storage_) T(std::move(*reinterpret_cast<T*>(other.storage_)));
constructed_ = true;
other.destroy();
}
}
InPlace& operator=(InPlace&& other) noexcept {
if (this != &other) {
destroy();
if (other.constructed_) {
new (storage_) T(std::move(*reinterpret_cast<T*>(other.storage_)));
constructed_ = true;
other.destroy();
}
}
return *this;
}
// 构造对象
template<typename... Args>
void construct(Args&&... args) {
if (constructed_) {
destroy();
}
new (storage_) T(std::forward<Args>(args)...);
constructed_ = true;
}
// 显式析构
void destroy() {
if (constructed_) {
reinterpret_cast<T*>(storage_)->~T();
constructed_ = false;
}
}
// 访问对象
T* get() {
return constructed_ ? reinterpret_cast<T*>(storage_) : nullptr;
}
const T* get() const {
return constructed_ ? reinterpret_cast<const T*>(storage_) : nullptr;
}
T& operator*() {
return *get();
}
const T& operator*() const {
return *get();
}
T* operator->() {
return get();
}
const T* operator->() const {
return get();
}
explicit operator bool() const {
return constructed_;
}
bool has_value() const {
return constructed_;
}
};
// 使用示例
struct Widget {
int id;
char name[32];
Widget(int i, const char* n) : id(i) {
std::snprintf(name, sizeof(name), "%s", n);
std::cout << "Widget " << id << " constructed\n";
}
~Widget() {
std::cout << "Widget " << id << " destructed\n";
}
void print() const {
std::cout << "Widget{id=" << id << ", name=" << name << "}\n";
}
void update(int new_id) {
id = new_id;
}
};
void basic_inplace_demo() {
std::cout << "=== Basic InPlace Demo ===\n\n";
InPlace<Widget> w;
std::cout << "Before construct: has_value = " << w.has_value() << "\n";
w.construct(1, "First Widget");
std::cout << "After construct: has_value = " << w.has_value() << "\n";
w->print();
w->update(10);
w.print();
std::cout << "\nLeaving scope (auto destruct)...\n";
}
void reconstruct_demo() {
std::cout << "\n=== Reconstruct Demo ===\n\n";
InPlace<Widget> w;
w.construct(1, "Version 1");
w->print();
std::cout << "\nReconstructing...\n";
w.construct(2, "Version 2");
w->print();
std::cout << "\nReconstructing again...\n";
w.construct(3, "Version 3");
w->print();
}
void conditional_init_demo() {
std::cout << "\n=== Conditional Initialization Demo ===\n\n";
InPlace<int> value;
bool use_default = true;
if (use_default) {
value.construct(42);
} else {
value.construct(100);
}
if (value) {
std::cout << "Value = " << *value << "\n";
}
}
// 可选值语义的InPlace(简化版std::optional)
template<typename T>
class Optional {
alignas(T) unsigned char storage_[sizeof(T)];
bool has_value_ = false;
public:
Optional() noexcept = default;
~Optional() {
reset();
}
Optional(const Optional& other) {
if (other.has_value_) {
new (storage_) T(*reinterpret_cast<const T*>(other.storage_));
has_value_ = true;
}
}
Optional(Optional&& other) noexcept {
if (other.has_value_) {
new (storage_) T(std::move(*reinterpret_cast<T*>(other.storage_)));
has_value_ = true;
other.reset();
}
}
template<typename U>
Optional(U&& value) {
new (storage_) T(std::forward<U>(value));
has_value_ = true;
}
Optional& operator=(const Optional& other) {
if (this != &other) {
reset();
if (other.has_value_) {
new (storage_) T(*reinterpret_cast<const T*>(other.storage_));
has_value_ = true;
}
}
return *this;
}
Optional& operator=(Optional&& other) noexcept {
if (this != &other) {
reset();
if (other.has_value_) {
new (storage_) T(std::move(*reinterpret_cast<T*>(other.storage_)));
has_value_ = true;
other.reset();
}
}
return *this;
}
void reset() {
if (has_value_) {
reinterpret_cast<T*>(storage_)->~T();
has_value_ = false;
}
}
template<typename U>
void emplace(U&& args) {
reset();
new (storage_) T(std::forward<U>(args));
has_value_ = true;
}
T& operator*() { return *reinterpret_cast<T*>(storage_); }
const T& operator*() const { return *reinterpret_cast<const T*>(storage_); }
T* operator->() { return reinterpret_cast<T*>(storage_); }
const T* operator->() const { return reinterpret_cast<const T*>(storage_); }
explicit operator bool() const { return has_value_; }
bool has_value() const { return has_value_; }
};
void optional_demo() {
std::cout << "\n=== Optional (Custom Implementation) Demo ===\n\n";
Optional<int> opt1;
std::cout << "opt1 has value: " << opt1.has_value() << "\n";
Optional<int> opt2(42);
std::cout << "opt2 has value: " << opt2.has_value() << "\n";
std::cout << "opt2 value: " << *opt2 << "\n";
opt1.emplace(100);
std::cout << "After emplace, opt1 value: " << *opt1 << "\n";
opt1.reset();
std::cout << "After reset, opt1 has value: " << opt1.has_value() << "\n";
// 移动语义
Optional<std::string> opt3(std::string("Hello"));
std::cout << "opt3 value: " << *opt3 << "\n";
Optional<std::string> opt4 = std::move(opt3);
std::cout << "After move, opt3 has value: " << opt3.has_value() << "\n";
std::cout << "opt4 value: " << *opt4 << "\n";
}
// 使用InPlace实现延迟初始化
class LazyWidget {
InPlace<Widget> widget_;
bool initialized_ = false;
public:
void init(int id, const char* name) {
if (!initialized_) {
widget_.construct(id, name);
initialized_ = true;
}
}
Widget* get() {
return initialized_ ? widget_.get() : nullptr;
}
bool is_initialized() const {
return initialized_;
}
void print() const {
if (initialized_) {
widget_->print();
} else {
std::cout << "Widget not initialized\n";
}
}
};
void lazy_init_demo() {
std::cout << "\n=== Lazy Initialization Demo ===\n\n";
LazyWidget lw;
std::cout << "Before init: " << lw.is_initialized() << "\n";
lw.print();
lw.init(99, "Lazy Widget");
std::cout << "After init: " << lw.is_initialized() << "\n";
lw.print();
}
int main() {
basic_inplace_demo();
reconstruct_demo();
conditional_init_demo();
optional_demo();
lazy_init_demo();
std::cout << "\n=== Key Takeaways ===\n";
std::cout << "1. InPlace wrapper automates placement new and destructor\n";
std::cout << "2. RAII ensures cleanup even on exceptions\n";
std::cout << "3. Supports reconstruction and move semantics\n";
std::cout << "4. Can implement optional/deferred initialization patterns\n";
std::cout << "5. Zero heap allocation, all memory on stack\n";
return 0;
}