(C++) C++ 中 shared_ptr weak_ptr

shared_ptr

std::shared_ptr<int> sp1 = new int();  // shared count = 1, weak count = 0
std::shared_ptr<int> sp2(sp1);         // shared count = 2, weak count = 0
std::shared_ptr<int> sp3 = sp2;        // shared count = 3, weak count = 0

std::weak_ptr<int> wp1(sp1);           // shared count = 3, weak count = 1
std::weak_ptr<int> wp2(wp1);           // shared count = 3, weak count = 2
std::weak_ptr<int> wp3 = wp2;          // shared count = 3, weak count = 3

shared_ptr weak_ptr 使用 reset 或者指向另一个 managed object导致 shared count或weak count相应的减一。

  1. 类继承中使用shared_ptr
class Base {};
class Derived : public Base {};
......
shared_ptr<Derived> dp1(new Derived);
shared_ptr<Base> bp1 = dp1;
shared_ptr<Base> bp2(dp1);
shared_ptr<Base> bp3(new Derived); 
  1. casting shared_ptr
shared_ptr<Base> base_ptr (new Base);
shared_ptr<Derived> derived_ptr;
// if static_cast<Derived *>(base_ptr.get()) is valid, then the following is valid:
derived_ptr = static_pointer_cast<Derived>(base_ptr);
  1. make_shared

使用shared_ptr = new int(),会导致两次内存分配:int对象的内存分配跟shared_ptr内部的 manager object 一次内存分配。make_shared 对此进行了优化,一次性分配 int + manager object 内存空间大小。
make_shared 用法:

shared_ptr<Thing> p (make_shared<Thing>(42, "I'm a Thing!"));
shared_ptr<Base> bp(make_shared<Derived1>());     // shared_ptr中的 template参数与make_shared中的tmeplate参数可以不一样(继承关系)

使用 weak_ptr

void do_it(weak_ptr<Thing> wp){
shared_ptr<Thing> sp = wp.lock(); // get shared_ptr from weak_ptr
if(sp)
sp->defrangulate(); // tell the Thing to do something
else
cout << "The Thing is gone!" << endl;
}

也可以直接从weak_ptr构建shared_ptr,这个时间如果weak_ptr过期(通过 weak_ptr::expired() 可以查询),则抛出异常:

void do_it(weak_ptr<Thing> wp){
shared_ptr<Thing> sp(wp); // construct shared_ptr from weak_ptr
// exception thrown if wp is expired, so if here, sp is good to go
sp->defrangulate(); // tell the Thing to do something
}

shared_ptr 线程安全

shared_ptr / weak_ptr 中的引用计数是线程安全的(atomic/memory barrier);但是跨线程赋值操作、reset操作没有线程安全。即

  1. shared_ptr 仅保证引用计数的正确性,在引用计数减为0之后,析构掉被管理对象;
  2. 在当前线程中进行赋值操作。不应在其他线程中进行赋值、reset操作;

enable_shared_from_this 及 shared_from_this

定义一个类,实现内存自动管理。两次调用 get 返回的是两个不同的shared_ptr,将导致同一个BadThing对象两次析构。

class BadThing {
public: 
  BadThing() = default;
  shared_ptr<BadThing> get() { return std::make_shared<BadThing>(); }
};
  1. enable_shared_from_this的实现
// enable_shared_from_this的实现
// 基于(/usr/include/c++/7.3.0/bits/shared_ptr.h)
// 此代码是对gcc实现的简化版本, 仅作为描述原理用.
template<typename T>
class enable_shared_from_this
{
public:
    shared_ptr<T> shared_from_this()
    {
        return shared_ptr<T>(this->weak_this);
    }
    shared_ptr<const T> shared_from_this() const
    {
        return shared_ptr<const T>(this->weak_this);
    }
private:
    template<typename>
    friend class shared_ptr;

    template<typename T1>
    void _M_weak_assign(T1* p, const shared_count<>& n)
    {
      weak_this._M_assign(p, n);
    }

    mutable weak_ptr<T> weak_this;
};

enable_shared_from_this 类中定义了一个 weak_ptr, 起到了上文提到的从obj指针生成 shared_ptr 对象的作用. 按照先前的原理, 我们可能认为是在obj初始化的时候, 同时对 weak_this 进行初始化, 但是在这段代码里显然没有对 weak_this 进行任何初始化工作(原始代码里也没有, gcc为什么不这样实现呢? 这是因为当对象没有由智能指针管理时, 这些操作是没有必要的. 所以应该把这个任务交给 shared_ptr). 第一个持有 Good 对象 g_obj 的 shared_ptr sp1 会对 g_obj 的 weak_this 进行处理, 使其有效. 具体见参考。

为什么

  1. 为什么需要 weak_ptr:编译期间解除A/B循环引用导致两者都不能释放内存,使用weak_ptr作为对方的引用 ref
  2. 为什么需要 enable_shared_from_this:特别的,在类内部需要调用函数(如创建线程等异步处理将自身作为参数);

参考

Using C++11’s Smart Pointers PDF
enable_shared_from_this类的作用和实现
std::shared_ptr std::weak_ptr 线程安全性
从 shared_from_this() 谈智能指针 weak_ptr 和 shared_ptr 的实现

posted @ 2022-07-23 10:21  山岚2013  阅读(88)  评论(0编辑  收藏  举报