You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

两种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 ...)这一行:

  1. 首先,初始化列表里的sock_(nullptr)已经把sock_初始化为一个空的unique_ptr了。
  2. 然后你写的sock_(new ...),是试图把sock_当成函数来调用,但不管是std::unique_ptr还是boost版本的unique_ptr,都没有重载函数调用运算符operator()
    这行代码本质上是语法错误——如果你的编译器没报错,大概率是环境的特殊情况,但即使编译通过了,它也不会做任何给sock_赋值/重置的操作,所以sock_还是保持初始化时的nullptr状态,调试器显示为空就很正常了。

正确的写法

如果一定要在构造函数体内设置sock_,应该用unique_ptrreset()方法:

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

火山引擎 最新活动