C++中unique_ptr为何看似可复制、按值传递?
你的核心认知误区
你完全混淆了两个概念:拷贝unique_ptr智能指针对象本身,和拷贝unique_ptr指向的底层托管对象。所有资料里提到的禁止复制规则,约束的是前者,而你的所有测试代码做的都是后者,当然不会触发报错。
先明确unique_ptr的拷贝禁止规则到底限制了什么
std::unique_ptr的核心设计语义是独占托管对象的所有权,因此它的拷贝构造函数、拷贝赋值运算符是被显式删除的,以下行为才是明确被禁止的:
- 用一个已存在的unique_ptr实例直接拷贝构造另一个unique_ptr实例
- 将unique_ptr实例按值作为函数参数传递(传参过程会触发拷贝构造)
- 将一个unique_ptr实例直接拷贝赋值给另一个已存在的同类型unique_ptr实例
- 在C++标准库算法中,如果操作要求拷贝容器内存储的unique_ptr元素(比如对存储unique_ptr的容器调用需要复制元素的拷贝类算法),会直接编译失败
逐行解释你的测试代码为什么能正常运行
你代码里的所有操作,全是在操作解引用后拿到的底层int值,从来没有触发过unique_ptr自身的拷贝逻辑:
int num1 = *uniquePtr1;:*uniquePtr1返回的是unique_ptr托管的int类型左值引用,你只是把这个int的值拷贝给了普通变量num1,和拷贝智能指针本身没有任何关系。int num2 = test(*uniquePtr1);:你传给test函数的是解引用后的int值,按值传递的是基础类型int,不是unique_ptr对象。如果你写test(uniquePtr1)才会触发编译错误——这才是按值传递unique_ptr的禁止写法。*ptr1 = *uniquePtr1;:同样是把uniquePtr1指向的int值,拷贝到裸指针ptr1指向的独立内存中,全程没有操作unique_ptr对象本身。*uniquePtr2 = *uniquePtr1;:这是最容易造成误解的一行。你在定义uniquePtr2时已经给它单独分配了一块新的int内存,这行代码做的是把uniquePtr1指向的int值,拷贝赋值到uniquePtr2指向的内存地址里。两个unique_ptr始终各自独占自己管理的内存,所有权没有发生任何转移或复制,根本不触发智能指针的拷贝赋值逻辑。如果你写uniquePtr2 = uniquePtr1;才是被禁止的操作——这行才是尝试把uniquePtr1本身拷贝赋值给uniquePtr2,会直接编译报错。
验证禁止规则的错误写法示例
你可以把以下代码加入你的测试文件,编译器会直接报「调用了被删除的函数」的错误,这才是资料里描述的被禁止的场景:
// 1. 尝试拷贝构造unique_ptr:编译失败 std::unique_ptr<int> uniquePtr3 = uniquePtr1; // 2. 尝试按值传递unique_ptr:编译失败 // 先修改test函数签名为int test(std::unique_ptr<int> p),再调用test(uniquePtr1) int test(std::unique_ptr<int> p) { return *p; } // 3. 尝试拷贝赋值unique_ptr:编译失败 uniquePtr2 = uniquePtr1;
另外提一句,你贴的测试代码有个笔误:你定义的裸指针变量名是ptr1,最后delete的却是ptr2,这会触发编译错误或者未定义行为,记得修正。
内容的提问来源于stack exchange,提问作者nyms1
相关产品推荐
相关产品推荐

