栈对象转堆内存复制方案疑问:非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这类无自定义构造/析构、无非静态成员指针的类型),处理复杂对象时会导致严重问题:
- 悬空指针风险:比如
std::string内部持有堆缓冲区,memcpy复制后,堆上的std::string和原栈对象指向同一块缓冲区。原栈对象销毁时会调用std::string的析构函数释放缓冲区,导致堆上的std::string持有悬空指针,后续访问会触发未定义行为。 - 无法正确销毁对象:堆上的复杂对象是通过
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
相关产品推荐
相关产品推荐

