如何从原始指针复制unique_ptr?游戏引擎实体复制报错求解
问题解答
1. 如何从原始指针复制std::unique_ptr?
首先得明确:std::unique_ptr是独占所有权的智能指针,它的设计核心就是不允许复制(拷贝构造函数被显式删除了),只能通过std::move转移所有权。
如果你的需求是基于一个原始指针创建新的unique_ptr,必须注意两个关键点:
- 原始指针必须指向动态分配的对象(比如用
new或std::make_unique创建的),绝对不能是栈上的局部对象——否则unique_ptr析构时会重复释放内存,触发未定义行为。 - 确保这个原始指针没有被其他智能指针管理,否则会造成双重释放。
举个接管原始堆指针的正确例子:
// 假设raw_ptr指向动态分配的Entity,且未被其他智能指针持有 std::unique_ptr<Entity> uPtr(raw_ptr);
如果你的真实需求是复制指针指向的对象(而非复制unique_ptr本身),那应该先拷贝对象,再用unique_ptr管理新对象:
// 基于原始指针指向的对象,拷贝出一个新的堆对象并由unique_ptr管理 std::unique_ptr<Entity> uPtr = std::make_unique<Entity>(*raw_ptr);
2. 游戏引擎中实体复制的正确实现
你的代码报错的核心原因就在这一行:
std::unique_ptr<Entity> uPtr{ &e };
这里的e是栈上的局部对象,当if代码块执行结束时,e会被自动销毁。而uPtr持有了这个栈对象的地址,后续entities中的unique_ptr在析构时会尝试delete这个地址——这是严重的内存错误,编译器会直接阻止这种危险操作,也就是你看到的“尝试引用已删除的函数”报错。
高效稳定的实现方案
假设你的Entity类支持拷贝构造(如果不支持,需要先实现一个正确的拷贝构造函数,确保所有属性、组件都被完整复制),可以这样写:
if (selectedEntity) { // 1. 在堆上拷贝选中的实体,用make_unique自动管理内存 auto new_entity = std::make_unique<Entity>(*selectedEntity); // 2. 修改新实体的ID new_entity->id = currentID++; // 3. 将unique_ptr移动到向量中(避免拷贝,效率更高) entities.emplace_back(std::move(new_entity)); }
为什么这是正确的?
std::make_unique<Entity>(*selectedEntity)会在堆上分配内存,调用Entity的拷贝构造函数创建新对象,unique_ptr会安全接管这个堆对象的所有权。emplace_back(std::move(new_entity))通过移动语义将unique_ptr的所有权转移到向量中,没有额外的拷贝开销,效率拉满。- 完全规避了栈对象的风险,所有被
unique_ptr管理的都是堆对象,内存安全有保障。
如果Entity不支持拷贝构造?
如果Entity因为包含不可拷贝的成员(比如其他unique_ptr)无法使用拷贝构造,你可以给Entity实现一个克隆方法:
class Entity { public: // 自定义克隆方法,手动复制所有属性 std::unique_ptr<Entity> clone() const { auto cloned = std::make_unique<Entity>(); cloned->id = this->id; cloned->components = this->components; // 假设components支持拷贝 // 复制其他需要的属性... return cloned; } };
然后这样调用即可:
if (selectedEntity) { auto new_entity = selectedEntity->clone(); new_entity->id = currentID++; entities.emplace_back(std::move(new_entity)); }
内容的提问来源于stack exchange,提问作者veridis_quo_t
相关产品推荐
相关产品推荐

