-
很多编程语言中垃圾回收(GC)的确是非常便利的特性,但它们执行的时间往往不可预期;而C++98的全手动资源管理又显得过于“原始”了一点。将能够自动进行操作(GC)以及操作的时机可预期(析构函数)这两个优势结合在一起的,就是C++11的
std::shared_ptr。 -
任一个
std::shared_ptr都不拥有它管理的对象;但它们共同确保对象不再被需要时会被销毁。其中主要的实现机制是 引用计数(reference count):一个资源被多少个std::shared_ptr指向的计数。std::shared_ptr的大部分构造函数会使该值增加1(移动构造除外,左右抵消不需要增减,因此也比复制更快),析构函数会使该值减小1,复制赋值运算符同时增减(左减右加)。如果std::shared_ptr发现经自减后引用计数变为0,则销毁该对象。 -
性能上来说:
std::shared_ptr的大小是两倍裸指针,一个指针指向资源,一个指向引用计数(严格说是包含引用计数的 control block,见下文)。- 引用计数占用的内存必须是动态分配的,因为被指向的对象本身对引用计数毫不知情,因此必须由
std::shared_ptr对引用计数进行动态分配和管理。(笔者注:C++的对象模型不像Java等有一个公共基类Object,因此如果采取将实现引用计数的责任甩给用户类编写者的设计,则会大大限制std::shared_ptr的使用场景,也无法支持 built-in types。) - 引用计数的自增和自减必须是原子操作,否则线程不安全,而原子操作相对较慢。
-
笔者注:到这里你可能会质疑循环引用的问题——的确,当出现循环引用时引用计数永远不会归零,造成内存泄漏。C++的解决办法是引入
std::weak_ptr,见下一节 Item 20 的讲解。 -
std::shared_ptr默认也使用delete作为资源释放方式,支持自定义 deleter。但与std::unique_ptr设计不同的是,std::shared_ptr的 deleter 不属于其类型的一部分,这样的设计使其更加灵活,例如可以将多个持有对象种类相同而 deleter 不同的对象互相赋值,或放在同一个容器中。(注:而std::unique_ptr的根本设计理念是 lightweight,zero-overhead,使用默认deleter还多占一个指针的空间是不可接受的。)
std::unique_ptr<Widget, decltype(customDeleter1)> upw(new Widget, customDeleter1);
std::shared_ptr<Widget> pw1(new Widget, customDeleter1);
std::shared_ptr<Widget> pw2(new Widget, customDeleter2);
std::vector<std::shared_ptr<Widget>> vpw{
pw1, pw2 };
pw

本文详细介绍了C++11的智能指针std::shared_ptr,讨论了其如何结合垃圾回收的便利性和析构函数的可预期性。std::shared_ptr通过引用计数管理资源,其大小为两倍裸指针,控制块包含引用计数、weakcount和deleter。文章提到了潜在的循环引用问题和解决方案,以及std::shared_ptr与std::unique_ptr的区别。此外,还阐述了std::shared_ptr的构造、赋值、析构以及与this指针的交互,强调了避免从裸指针创建std::shared_ptr的重要性。
最低0.47元/天 解锁文章
1225

被折叠的 条评论
为什么被折叠?



