std::unique_ptr嵌套场景下std::move底层实现机制问询
std::move在unique_ptr嵌套场景下的执行逻辑解析
定义的结构体
struct Foo{ int val = 0; std::unique_ptr<Foo> child_a = nullptr; std::unique_ptr<Foo> child_b = nullptr; Foo(int val_):val(val_){} ~Foo(){std::cout<<"Deleting foo "<<val<<std::endl;} };
(注:将原代码中的NULL替换为C++更推荐的nullptr,语义完全一致)
构建树形结构的代码
std::unique_ptr<Foo> root = std::make_unique<Foo>(0); root->child_a = std::make_unique<Foo>(1); root->child_b = std::make_unique<Foo>(2); root->child_a->child_a = std::make_unique<Foo>(3); root->child_a->child_b = std::make_unique<Foo>(4); root->child_b->child_a = std::make_unique<Foo>(5); root->child_b->child_b = std::make_unique<Foo>(6);
(注:原代码中std::make_unique<Foo>(Foo(0))可简化为std::make_unique<Foo>(0),直接传递构造参数即可避免多余拷贝)
执行root = std::move(root->child_a)的具体过程
- 转换右值引用:
std::move(root->child_a)仅将root->child_a这个unique_ptr转换成右值引用,这一步不会销毁任何对象,只是标记该指针允许被移动。 - 执行移动赋值:
root的移动赋值运算符会接管右值引用指向的对象(即val=1的Foo节点),此时root的所有权转移到val=1的节点及其子节点(val=3、4)上。- 紧接着,
root原本持有的val=0的Foo对象会被释放:- 调用val=0对象的析构函数,析构时会依次处理它的成员
child_a和child_b:- 由于
child_a已经被移动,内部指针被置为空,销毁这个unique_ptr时无任何操作; child_b仍指向val=2的节点,因此触发val=2节点的析构,进而递归销毁val=2的child_a(val=5)和child_b(val=6)。
- 由于
- 调用val=0对象的析构函数,析构时会依次处理它的成员
- 最终结果:val=1、3、4的节点被
root保留,val=0、2、5、6的节点被销毁,输出对应的日志信息。
权威学习资源
- 《C++ Primer》(第5版):第12章“动态内存”、第13章“拷贝控制”详细讲解了
unique_ptr的所有权语义、移动操作的核心逻辑。 - 《Effective Modern C++》:条款23“理解std::move和std::forward”、条款18“使用std::unique_ptr管理独占资源”,深入剖析了移动语义的本质和智能指针的正确用法。
- C标准文档(C11及后续版本):关于
std::unique_ptr移动赋值运算符的规范,明确了移动操作会转移所有权并置空原指针的行为。
内容的提问来源于stack exchange,提问作者user3353819
相关产品推荐
相关产品推荐

