美文网首页
关于shared_ptr

关于shared_ptr

作者: 丑角的晨歌 | 来源:发表于2020-06-23 21:12 被阅读0次

    简单总结,详见参考资料。

    引用计数的实现方式

    引用计数是与对象绑定的,并且可能有多个shared_ptr绑定同一对象,为了达到同时更新多个shared_ptr的引用计数,所以需要让他们指向同一个引用计数,所以只能在堆上另外分配空间来存放引用计数。
    为了保证多线程读同一个shared_ptr是安全的,引用计数的增减是原子操作。

    线程安全性

    shared_ptr 的线程安全级别和内建类型、标准库容器、std::string 一样,即:
    一个 shared_ptr 对象实体可被多个线程同时读取;
    两个 shared_ptr 对象实体可以被两个线程同时写入,“析构”算写操作;
    如果要从多个线程读写同一个 shared_ptr 对象,那么需要加锁;

    为什么shared_ptr不能并发读写:因为 shared_ptr 有两个数据成员(引用计数、对象指针),读写操作不能原子化。
    具体发生多线程问题的场景可以自己推导,更详细的说明可以看陈硕写的文章:
    为什么多线程读写 shared_ptr 要加锁?

    enable_shared_from_this的实现

    一个主要的场景是保证异步回调函数中操作的对象仍然有效。
    std::enable_shared_from_this<T> 有一个std::weak_ptr<T>的成员,实际上在构造std::enable_shared_from_this<T>时,并没有初始化std::weak_ptr<T>成员,而是在用这个std::enable_shared_from_this<T>去构造std::shared_ptr的时候,去构造并初始化这个std::weak_ptr<T>成员。所以这也就是为什么cppreference中说的这个对象必须是std::shared_ptr管理的,因为这个对象不是通过std::shared_ptr来管理,那么std::weak_ptr是未初始化的,无法通过其提升为std::shared_ptr对象。
    如何判断是否继承了shared_from_this?模板类型推导:根据是否有继承特定类型实例化不同的模板实现;
    多继承场景下使用虚继承解决问题;

    make_shared/new

    与直接使用new相比,make函数不存在代码重复且具备异常安全性,std::make_shared和std::allocate_shared可以生成更快更短的目标码。
    对象和控制块分配在一块内存上,减少了内存分配的次数。但是这并不一定是好事,因为这导致对象和控制块占用的内存也要一次回收掉。即,如果还有std::weak_ptr存在,控制块就要在,对象占用的内存也没办法回收。如果对象比较大,且std::weak_ptr在对象析构后还可能长期存在,那么这种开销是不可忽视的。
    在需要自定义删除器及以大括号初始化时make函数无能为力。
    更详细可以阅读《Effective Moden C++》Item 21。

    pointer_cast的实现

    智能指针的类型转换无法通过static_cast等实现,需要使用static_pointer_cast方法(想一下如果取裸指针出来static_cast会发生什么)。static_pointer_cast是通过shared_ptr一个特殊的构造函数实现的。

    template< class T, class U > 
    std::shared_ptr<T> static_pointer_cast( const std::shared_ptr<U>& r ) noexcept
    {
        auto p = static_cast<typename std::shared_ptr<T>::element_type*>(r.get());
        return std::shared_ptr<T>(r, p);
    }
    

    shared_ptr与deleter

    shared_ptr<Foo> sp(new Foo)在构造sp的时候捕获了Foo的析构行为,意味着:

    1. shared_ptr<void> 可以指向并安全地管理(析构或防止析构)任何对象,派生类没有虚析构函数也能被正确析构

      shared_ptr<Foo> sp1(new Foo); // ref_count.ptr 的类型是 Foo*
      shared_ptr<void> sp2 = sp1; // 可以赋值,Foo* 向 void* 自动转型
      sp1.reset(); // 这时 Foo 对象的引用计数降为 1

    此后 sp2 仍然能安全地管理 Foo 对象的生命期,并安全完整地释放 Foo,不会出现 delete void* 的情况,因为 delete 的是 ref_count.ptr,不是 sp2.ptr。
    std::shared_ptr<void>的工作原理

    1. shared_ptr进行资源销毁时,总会调用创建智能指针的那个DLL中的delete,这意味着shared_ptr可以随意地在DLL间传递而不需担心跨DLL的问题。(Effective C++ Item18)

    参考资料:
    为什么多线程读写 shared_ptr 要加锁
    std::shared_ptr<void>的工作原理
    《Effective C++》
    《Effective Modern C++》

    相关文章

      网友评论

          本文标题:关于shared_ptr

          本文链接:https://www.haomeiwen.com/subject/otjgtktx.html