跳转至

嵌入式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;
}