为何make_shared创建的对象在weak_ptr存活时仍会调用析构函数?
问题
理论上,make_shared()与直接构造shared_ptr的区别在于内存分配方式:make_shared()会一次性分配一块内存同时存放对象数据和引用控制块,而直接用shared_ptr<T>(new T)的方式会分别为对象和控制块分配两块独立内存。make_shared()的一个公认缺点是,只要还有weak_ptr存活,那整块包含对象和控制块的内存就无法被释放。但我编写的程序里,不管用哪种方式创建shared_ptr,输出结果都完全一致;而且在所有shared_ptr都调用reset()后,明明还有weak_ptr存活,对象的析构函数却被正常调用了。附上测试代码:
#include <iostream> #include <memory> class mc { public: mc(int p):p_{p} {std::cerr<<"Constructor"<<std::endl;} ~mc() { std::cerr<<"Destructor"<<std::endl;} private: int p_; }; std::weak_ptr<mc> wp3; int main() { auto sp=std::make_shared<mc>(4); auto sp2=sp; auto sp3{sp}; wp3=std::weak_ptr<mc>{sp}; sp2.reset(); sp3.reset(); sp.reset(); for (int i =0;i< 5;i++) {std::cerr<<sp.use_count()<<std::endl;} return 0; }
解析
- 你混淆了对象析构和内存释放两个概念:
make_shared()的内存问题不是阻止对象析构,而是延迟整块内存的回收。当最后一个shared_ptr被reset或销毁时,对象的强引用计数降为0,对象的析构函数会立即执行——这和有没有weak_ptr存活完全无关。只有当最后一个weak_ptr也被销毁后,make_shared()分配的那块同时包含对象和控制块的内存才会被操作系统回收。而用shared_ptr<T>(new T)的方式,对象内存会在强引用计数为0时就被释放,控制块则要等到所有weak_ptr都销毁后才释放。 - 你程序里的
wp3是全局变量,会在main函数执行完毕后才销毁。但你看到的析构函数调用发生在main里所有shared_ptrreset之后,这完全符合预期,因为对象析构只由强引用计数决定。 - 至于两种创建方式输出一致,是因为你的测试逻辑只检测了析构函数调用和
shared_ptr的use_count()。use_count()返回的是强引用计数,weak_ptr的存在不会影响这个值,所以两种方式下输出自然相同。如果要验证内存释放的差异,需要借助内存分析工具(比如Valgrind),或者自定义内存分配器来跟踪内存的分配和释放时机。
内容的提问来源于stack exchange,提问作者Fzza
相关产品推荐
相关产品推荐

