C++17中含引用成员的struct如何正确实现深拷贝
问题根因
- 引用类型(包括左值引用、右值引用)本身不持有对象的所有权,仅作为目标对象的别名存在。你代码中将
inner成员声明为Inner&&,绑定到临时Inner对象后,临时对象在所在表达式执行结束就会被析构,导致inner变成悬垂引用,输出乱码是访问已销毁内存的典型表现。 - 拷贝构造函数中
std::move(copy(a.inner))返回的是临时Inner对象,在构造函数执行结束后立即销毁,b.inner同样会变成悬垂引用。
适配方案
你的需求可以通过「私有智能指针持有实例+公有引用成员绑定实例」的结构实现,完全满足支持前置声明、无需额外解引用、深拷贝三个要求。
完整修改代码
#include <iostream> #include <memory> #include <utility> struct Inner; struct Outer { Inner& inner; Outer(Inner&& a); Outer(const Outer& a); Outer& operator=(const Outer& a); ~Outer(); private: std::unique_ptr<Inner> inner_storage; }; struct Inner { int v; }; Outer::Outer(Inner&& a) : inner_storage(std::make_unique<Inner>(std::move(a))) , inner(*inner_storage) { std::cout << " -- Constructor 1 --" << std::endl; } Outer::Outer(const Outer& a) : inner_storage(std::make_unique<Inner>(*a.inner_storage)) , inner(*inner_storage) { std::cout << " -- Constructor 2 --" << std::endl; } Outer& Outer::operator=(const Outer& a) { if (this != &a) { *inner_storage = *a.inner_storage; } return *this; } Outer::~Outer() = default; int main() { Outer a = {Inner {30}}; std::cout << a.inner.v << std::endl; // 预期输出: 30 a.inner.v += 1; std::cout << a.inner.v << std::endl; // 预期输出: 31 Outer b = a; // 将a拷贝给b std::cout << a.inner.v << std::endl; // 预期输出: 31 std::cout << b.inner.v << std::endl; // 预期输出: 31 b.inner.v += 1; std::cout << a.inner.v << std::endl; // 预期输出: 31 std::cout << b.inner.v << std::endl; // 预期输出: 32 return 0; }
运行结果
-- Constructor 1 -- 30 31 -- Constructor 2 -- 31 31 31 32
完全符合预期。
实现说明
std::unique_ptr<Inner>支持不完整类型,仅需将析构、拷贝构造等特殊函数的实现放到Inner类型定义之后即可,适配前置声明的需求。- 公开的
inner成员是左值引用,直接绑定到inner_storage持有的实例,访问语法和你原有代码完全一致,不需要额外加*/->,不会增加代码生成复杂度。 - 拷贝构造/拷贝赋值时均做深拷贝,两个
Outer实例的inner指向完全独立的对象,没有别名问题。
内容的提问来源于stack exchange,提问作者Jamie
相关产品推荐
相关产品推荐

