《Effective Modern C++》学习笔记 - Item 19: 使用 std::shared_ptr 管理共享性资源(附MSVC源码解析)

本文详细介绍了C++11的智能指针std::shared_ptr,讨论了其如何结合垃圾回收的便利性和析构函数的可预期性。std::shared_ptr通过引用计数管理资源,其大小为两倍裸指针,控制块包含引用计数、weakcount和deleter。文章提到了潜在的循环引用问题和解决方案,以及std::shared_ptr与std::unique_ptr的区别。此外,还阐述了std::shared_ptr的构造、赋值、析构以及与this指针的交互,强调了避免从裸指针创建std::shared_ptr的重要性。
  • 很多编程语言中垃圾回收(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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值