You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++11及以上创建未初始化栈内存的合规可移植方法

栈上创建未初始化对象数组的实现与标准合规性分析

你提供的实现代码

#include <iostream>
#include <string>
#include <array>
#include <cstddef>
#include <new>

struct Object
{
    std::size_t m_id = 0;
    std::string m_name = "";

    Object()
    {
        std::cout << "I'm Alive\n";
    }

    Object(const int id, const std::string& name)
        : m_id(id)
        , m_name(name)
    {
        std::cout << "I am " << m_name << " # " << m_id << "\n";
    }

    Object(const Object& o)
        : m_id(o.m_id)
        , m_name(o.m_name)
    {
        std::cout << "Copied\n";
    }

    ~Object()
    {
        std::cout << "Goodbye for now\n";
    }
};


void stackAllocationExample()
{
    static constexpr std::size_t nObjects = 10;
    static constexpr std::size_t arraySize = nObjects * sizeof(Object);


    alignas(std::array<Object, nObjects>) std::array<std::byte, arraySize> objects;
    std::array<std::string, nObjects> names = {"O1", "O2", "O3", "O4", "O5", "O6", "O7", "O8", "O9", "O10"};

    for (std::size_t i = 0; i < nObjects; ++i)
    {
        new (objects.data() + sizeof(Object) * i) Object(i+1, names[i]);
    }

    for (std::size_t i = 0; i < nObjects; ++i)
    {
        std::byte* rawBytes = &(objects[0]) + sizeof(Object) * i;
        Object* obj = std::launder((Object*)rawBytes);
        std::cout << i << ": {" << obj->m_id << ", " << obj->m_name << "}\n";
        obj->~Object();
    }
 }

int main()
{
    stackAllocationExample();
    return 0;
}

该实现的标准合规性与跨编译器支持

这个实现符合C++17及以后的标准,核心合规点如下:

  • 用std::array<std::byte, arraySize>作为内存缓冲区,配合alignas(std::array<Object, nObjects>)确保内存对齐满足Object数组的要求,完全符合标准中关于内存对齐的规定。
  • 使用placement new在缓冲区内存上逐个构造Object对象,这是标准中显式构造对象的合法方式。
  • 构造完成后调用std::launder获取有效指针:原缓冲区是std::byte类型数组,新构造的Object类型与原类型不同,std::launder用于告知编译器该内存位置的对象已变更,避免优化导致的未定义行为,这是C++17标准针对此类场景的正确用法。
  • 最后手动调用~Object()析构对象,符合placement new构造对象后需手动析构的要求。

跨编译器支持方面,主流编译器(GCC 7+、Clang 5+、MSVC 2017+)都完整支持C++17的这些特性,因此该实现可以在这些编译器上正常运行。

C++17引入std::launder前的实现方式

C++17之前没有std::byte和std::launder,可以通过以下方式实现栈上未初始化对象数组:

  1. 用unsigned char数组代替std::byte数组作为内存缓冲区——标准允许char/unsigned char用于存储其他对象的原始字节。
  2. 用alignas(Object)直接保证内存对齐到Object类型的要求。
  3. 指针转换时直接使用reinterpret_cast<Object*>——C17之前针对char/unsigned char缓冲区上构造的对象,主流编译器会认可这种转换的有效性(尽管标准在此处的规定不如C17明确,但实践中是安全的)。

对应的实现代码如下:

#include <iostream>
#include <string>
#include <array>
#include <cstddef>
#include <new>

struct Object
{
    std::size_t m_id = 0;
    std::string m_name = "";

    Object()
    {
        std::cout << "I'm Alive\n";
    }

    Object(const int id, const std::string& name)
        : m_id(id)
        , m_name(name)
    {
        std::cout << "I am " << m_name << " # " << m_id << "\n";
    }

    Object(const Object& o)
        : m_id(o.m_id)
        , m_name(o.m_name)
    {
        std::cout << "Copied\n";
    }

    ~Object()
    {
        std::cout << "Goodbye for now\n";
    }
};


void stackAllocationExample()
{
    static constexpr std::size_t nObjects = 10;
    static constexpr std::size_t arraySize = nObjects * sizeof(Object);

    // 用unsigned char数组作为缓冲区,alignas保证对齐
    alignas(Object) unsigned char objects[arraySize];
    std::array<std::string, nObjects> names = {"O1", "O2", "O3", "O4", "O5", "O6", "O7", "O8", "O9", "O10"};

    for (std::size_t i = 0; i < nObjects; ++i)
    {
        new (objects + sizeof(Object) * i) Object(i+1, names[i]);
    }

    for (std::size_t i = 0; i < nObjects; ++i)
    {
        unsigned char* rawBytes = objects + sizeof(Object) * i;
        // C++17前直接用reinterpret_cast转换指针
        Object* obj = reinterpret_cast<Object*>(rawBytes);
        std::cout << i << ": {" << obj->m_id << ", " << obj->m_name << "}\n";
        obj->~Object();
    }
 }

int main()
{
    stackAllocationExample();
    return 0;
}

内容的提问来源于stack exchange,提问作者Malphrush

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 00:43:13