Aliasing constructor of shared_ptr

C++11 标准库提供的shared_ptr有这么一个构造函数:

template< class Y > 
shared_ptr( const shared_ptr<Y>& r, T *ptr );

cppreference.com中对他的描述是这样的

The aliasing constructor: constructs a shared_ptr which shares ownership information with r, but holds an unrelated and unmanaged pointer ptr. Even if this shared_ptr is the last of the group to go out of scope, it will call the destructor for the object originally managed by r. However, calling get() on this will always return a copy of ptr. It is the responsibility of the programmer to make sure that this ptr remains valid as long as this shared_ptr exists, such as in the typical use cases where ptr is a member of the object managed by r or is an alias (e.g., downcast) of r.get()

再加上《C++标准库》中的这么一个例子

struct X { }

shared_ptr<X> sp1(new X);

 // ERROR: delete for this X will never be called
shared_ptr<X> sp2(sp1, new X);

sp1.reset();

// use_count() == 0, but get() != nullptr
shared_ptr<X> sp3(sp1, new X);  

不知道各位看到这些后是什么感受,反正我呢,是没看懂啦。本着爱折腾的性子,便有了接下来的这个一个试验:

#include <iostream>
#include <memory>
#include <string>
using namespace std;

struct X {
    int a;
    X(int aa): a(aa) { }

    ~X() {
        cout << "~X() " << a << endl;
    }
};

int main() {
    shared_ptr<X> sp1(new X(1),
                      [](X *x) {
                          cout << "deleting" << endl;
                          delete x;
                      });
    shared_ptr<X> somePtr(sp1, new X(-1));
    shared_ptr<X> sp2(sp1, new X(2));

    cout << "sp1.use_count() " << sp1.use_count() << endl     // 3
         << "sp2.use_count() " << sp2.use_count() << endl;    // 3

    sp1.reset();
    cout << endl << "after sp1 reset" << endl;

    cout << "sp1.use_count() " << sp1.use_count() << endl     // 0
         << "sp2.use_count() " << sp2.use_count() << endl;    // 2

    shared_ptr<X> sp3(sp1, new X(3));
    cout << endl << "sp3 constructed" << endl;
    cout << "sp1.use_count() " << sp1.use_count() << endl     // 0
         << "sp2.use_count() " << sp2.use_count() << endl     // 2
         << "sp3.use_count() " << sp3.use_count() << endl;    // 0

    if (sp3.get() != nullptr) {
        cout << "sp3.get() != nullptr" << endl << endl;
    }
}
// output:
sp1.use_count() 3
sp2.use_count() 3

after sp1 reset
sp1.use_count() 0
sp2.use_count() 2

sp3 constructed
sp1.use_count() 0
sp2.use_count() 2
sp3.use_count() 0
sp3.get() != nullptr

deleting
~X() 1


现在,可以好好扯一扯了。这里sp2的用法和《C++标准库》中的那里例子类似,只是多打印了一些调试信息。

我们可以看到,第一次sp1.use_count() == 3,很显然,这里有sp1 sp2 someotherPtr,共三个。然后,我们reset sp1。现在,sp1并不拥有任何对象,于是,sp1.use_count() == 0。而sp2.use_count() == 2

由于没有看过他的源码,所以,这里姑且说,sp2someotherPtr共享那个“ownership”,而sp1则已放弃了。这里的“ownership”,其实便是上面例子所构造的第一个X(2)对象(后面会说明,确实是那个X)。


现在,我们又弄多了一个sp3。现在就有趣多了。sp3.use_count()竟然是0。不过这其实也没有太让人惊讶,毕竟,上面的文档就已经说了,aliasing constructor会共享ownership。既然sp1都已经被我们resetuse_count()当然会返回零。

随后,main()函数退出,sp3 sp2 someotherPtr依次被销毁,someotherPtr销毁的时候,像文档中所说的,他销毁的,是sp1原先所管理的对象(即X(2))。这里还有一点值得注意,
someotherPtr所使用的deleter,也是一开始我们赋予sp1的。由此可见,文档中的ownership,不仅包括原始shared pointer 所管理的资源,还包括那个deleter。至于这“共享”发生了什么故事在里面,只能等以后研究了其源码再来聊一聊了。


在最后,再说多一句,以上并不是aliasing constructor的正确用法,所以,还是不要玩火为妙。至于其真正用法,《C++标准库》给了一个简短的例子。个人觉得,这个的例子更为详尽一些。

你可能感兴趣的:(C++,shared-ptr)