两种unique_ptr用法差异及类构造中unique_ptr初始化失败原因咨询
关于
unique_ptr的两个问题解答 问题1:两种unique_ptr用法的区别
结合你第二个问题里的代码,我猜你说的两种用法是「初始化列表直接构造unique_ptr」和「默认构造后尝试修改指向」?如果是这样的话,核心区别主要在这几点:
- 初始化时机不同:
初始化列表的写法是在类成员的构造阶段就完成了unique_ptr的初始化,相当于给sock_直接“绑定”了新创建的socket对象,这完全符合C++对象的初始化规则——成员会在进入构造函数体之前就准备好。
而如果是先默认构造空的unique_ptr再修改,那是在构造函数体执行阶段才去调整它的指向,属于“修改已有对象”而非“初始化”。 - 语义与安全性:
初始化列表的写法语义更清晰:一眼就能看出这是成员的初始值,而且如果socket构造抛出异常,unique_ptr会自动处理资源释放,不会有内存泄漏风险。
后者的reset()写法虽然也能达到目的,但语义上是“重置”已有指针,适合需要动态改变指向的场景,而非初始化场景。
问题2:为什么第二种构造函数里sock_是空的?
咱们直接拆解两段代码的本质:
第一段能正常工作的代码
a::a() : service_(), sock_(new boost::asio::ip::tcp::socket(service_)) { }
这里用的是成员初始化列表,sock_作为类的成员,在构造函数体执行前,就通过unique_ptr的「接受原始指针」的构造函数完成了初始化——直接把新创建的socket的所有权交给了sock_,完全符合unique_ptr的设计逻辑,所以能正常工作。
第二段失效的代码
a::a() : service_(), sock_(nullptr) { sock_(new boost::asio::ip::tcp::socket(service_)); }
问题出在函数体内的sock_(new ...)这一行:
- 首先,初始化列表里的
sock_(nullptr)已经把sock_初始化为一个空的unique_ptr了。 - 然后你写的
sock_(new ...),是试图把sock_当成函数来调用,但不管是std::unique_ptr还是boost版本的unique_ptr,都没有重载函数调用运算符operator()!
这行代码本质上是语法错误——如果你的编译器没报错,大概率是环境的特殊情况,但即使编译通过了,它也不会做任何给sock_赋值/重置的操作,所以sock_还是保持初始化时的nullptr状态,调试器显示为空就很正常了。
正确的写法
如果一定要在构造函数体内设置sock_,应该用unique_ptr的reset()方法:
a::a() : service_(), sock_(nullptr) { sock_.reset(new boost::asio::ip::tcp::socket(service_)); }
或者更推荐用make_unique(C++14及以上支持,boost也有boost::make_unique),它的异常安全性更好:
a::a() : service_(), sock_(boost::make_unique<boost::asio::ip::tcp::socket>(service_)) { }
内容的提问来源于stack exchange,提问作者B C




