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

栈对象转堆内存复制方案疑问:非POD类型的析构问题

栈对象复制到堆内存的析构函数调用问题

我需要将栈对象复制到新分配的堆内存中,因为栈对象会在作用域结束时销毁,而这些数据要留到命令系统的下一帧处理。我对原生内存复制不熟悉,已经写了一套实现,但不确定稳定性,尤其是处理包含复杂对象的结构体时,想确认逻辑是否正确。

现有实现代码

定义MemBlock结构体

struct MemBlock {
    void*  data  = nullptr;
    size_t size  = 0;
};

实现内存复制函数

void CopyMemBlockArray(const std::vector<MemBlock>& src, std::vector<MemBlock>* dst) {
        
        // 目标为空则无法复制
        if (!dst)
            return;

        for (auto& block : src) {
            // 为复制的块分配内存
            MemBlock copiedBlock;
            copiedBlock.size = block.size;
            copiedBlock.data = malloc(block.size);

            // 将原块数据复制到分配的内存中
            memcpy(copiedBlock.data, block.data, block.size);
            dst->push_back(copiedBlock);
        }
    }

使用示例

void MyFoo() {
    int                value  = 1;
    unsigned long long value2 = 3242424;

    // 构造原数据数组
    std::vector<MemBlock> argv;
    argv.push_back({ &value,  sizeof(int) });
    argv.push_back({ &value2, sizeof(unsigned long long) });

    std::vector<MemBlock>* copiedArgv = new std::vector<MemBlock>();
    CopyMemBlockArray(argv, copiedArgv); // 注:原示例传&copiedArgv是错误的,参数类型不匹配

    // 模拟跨帧处理(执行其他操作)

    // 释放内存
    int*                pValue  = (int*) copiedArgv->at(0).data;
    unsigned long long* pValue2 = (unsigned long long*) copiedArgv->at(1).data;
    free(pValue);
    free(pValue2);
    delete copiedArgv; // 原示例遗漏了释放vector本身
}

问题场景

当复制包含复杂对象的结构体时,比如:

struct A {
   int i = 5;
   std::string text = "some text"; // 原栈对象销毁后,内部char*会失效,复制后的对象会持有悬空指针
}

struct B {
   MyObject customObject;
   A a;
}

通过CopyMemBlockArray复制这类结构体后,调用free()是否会触发它们的析构函数?


回答

核心结论

调用free()完全不会触发任何析构函数。free是C标准库的函数,仅负责释放malloc分配的内存块,对C++对象的构造/析构逻辑完全无感知。

现有实现的问题

你的实现本质是字节级浅拷贝,仅适用于平凡类型(如int、unsigned long long这类无自定义构造/析构、无非静态成员指针的类型),处理复杂对象时会导致严重问题:

  1. 悬空指针风险:比如std::string内部持有堆缓冲区,memcpy复制后,堆上的std::string和原栈对象指向同一块缓冲区。原栈对象销毁时会调用std::string的析构函数释放缓冲区,导致堆上的std::string持有悬空指针,后续访问会触发未定义行为。
  2. 无法正确销毁对象:堆上的复杂对象是通过malloc分配内存后直接字节复制的,从未执行过构造函数初始化。即使你尝试用delete替代free,也会因为对象未正确构造而触发未定义行为。

修正方案

要处理复杂类型,必须保证对象的构造/析构逻辑被正确执行,可通过类型擦除+拷贝构造实现:

// 带析构器的MemBlock,支持复杂类型
struct MemBlock {
    void* data = nullptr;
    size_t size = 0;
    void (*deleter)(void*) = nullptr; // 存储对应类型的析构+释放逻辑

    // 模板方法:创建堆上的拷贝对象
    template<typename T>
    static MemBlock create(const T& obj) {
        T* ptr = new T(obj); // 调用拷贝构造函数初始化对象
        return {
            ptr,
            sizeof(T),
            [](void* p) { delete static_cast<T*>(p); } // 绑定析构+内存释放
        };
    }

    // 销毁当前块
    void destroy() {
        if (deleter && data) {
            deleter(data);
            data = nullptr;
            size = 0;
            deleter = nullptr;
        }
    }
};

// 修改复制函数:直接复制MemBlock结构(内部已包含正确的拷贝和销毁逻辑)
void CopyMemBlockArray(const std::vector<MemBlock>& src, std::vector<MemBlock>* dst) {
    if (!dst) return;
    dst->reserve(src.size());
    for (const auto& block : src) {
        dst->push_back(block);
    }
}

// 使用示例
void MyFoo() {
    A a_obj;
    std::vector<MemBlock> argv;
    argv.push_back(MemBlock::create(a_obj)); // 正确创建堆上的拷贝

    std::vector<MemBlock> copiedArgv;
    CopyMemBlockArray(argv, &copiedArgv);

    // 跨帧处理

    // 销毁所有块,自动调用对应对象的析构函数
    for (auto& block : copiedArgv) {
        block.destroy();
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 10:12:32