关于智能指针的demo
,参见C++ primer 12.1
。
什么是智能指针 智能指针是使用RAII
手法对裸指针进行的一个面向对象封装,即在构造函数中初始化资源地址,在析构函数中释放资源。智能指针保证当资源应当被释放的时候 一定会释放它,这是利用了栈上的对象出作用域时自动析构 这个特点。
为什么引入智能指针 用以弥补裸指针的不足:
1)使用裸指针分配内存后,没有对指针释放资源会导致内存泄漏;
2)多个裸指针指向同一资源时,多次释放资源时,对空悬指针进行释放会导致不可预知的错误。
如何知道是否存在内存泄漏 在不借助其他检测工具的情况下,可以通过观察,即程序长时间运行后内存占用率一直不断的缓慢的上升,而实际上在你的逻辑中并没有这么多的内存需求。
如何定位到内存泄漏点
review
代码,找到new
和delete
关键字的位置,先看看内存的申请和释放是否是成对的来进行初步的判断;
对于函数中申请的临时空间,认真检查是否存在提前跳出函数的地方而导致没有释放内存。
智能指针有哪些 智能指针分为不带引用计数的scoped_ptr
和unique_ptr
,带引用计数的shared_ptr
和weak_ptr
。
这些智能指针分别是如何实现的 scoped_ptr
私有化了拷贝构造函数和赋值操作运算符,资源的所有权无法进行转移,也无法在容器中使用,这种方式杜绝了浅拷贝的发生。
unique_ptr
删除了拷贝构造函数和赋值函数,因此不支持普通的拷贝或赋值操作。但引入了移动构造函数和移动赋值运算符。所以它们保证了有唯一的智能指针持有此资源。unique_ptr
还提供了reset
重置资源,swap
交换资源等函数,也经常会使用到。
shared_ptr
称为强智能指针,它的资源引用计数器在内存的heap
堆上 (这保证了,每个智能指针的引用计数变量会动态的变化)。通常用于管理对象的生命周期 。只要有一个指向对象的shared_ptr
存在,该对象就不会被析构。
weak_ptr
被称为弱智能指针,其对资源的引用不会引起资源的引用计数的变化 ,通常作为观察者,用于判断资源是否存在,并根据不同情况做出相应的操作。比如使用weak_ptr
对资源进行弱引用,当调用weak_ptr
的lock()
方法时,若返回nullptr
,则说明资源已经不存在,放弃对资源继续操作。否则,将返回一个shared_ptr
对象,可以继续操作资源。另外,一旦最后一个指向对象的shared_ptr
被销毁,对象就会被释放。即使有weak_ptr
指向对象,对象也还是会被释放。
当需要多个智能指针 指向同一个资源时,使用带引用计数的智能指针。每增加一个智能指针指向同一资源,资源引用计数加1
,反之减1
。当引用计数为0
时,由最后一个指向资源的智能指针将资源进行释放。
如何避免循环引用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 #include <iostream> #include <memory> using namespace std ;class B ; class A { public : A() { cout << "A()" << endl ; } ~A() { cout << "~A()" << endl ; } shared_ptr <B> _ptrb; }; class B { public : B() { cout << "B()" << endl ; } ~B() { cout << "~B()" << endl ; } shared_ptr <A> _ptra; }; int main () { shared_ptr <A> ptra (new A()) ; shared_ptr <B> ptrb (new B()) ; ptra->_ptrb = ptrb; ptrb->_ptra = ptra; cout << ptra.use_count() << endl ; cout << ptrb.use_count() << endl ; return 0 ; }
解决办法,这也是强弱智能指针的一个重要应用规则:定义对象时,用强智能指针shared_ptr
,在其它地方引用对象时,使用弱智能指针weak_ptr
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 #include <iostream> #include <memory> using namespace std ;class B ; class A { public : A() { cout << "A()" << endl ; } ~A() { cout << "~A()" << endl ; } weak_ptr<B> _ptrb; }; class B { public : B() { cout << "B()" << endl ; } ~B() { cout << "~B()" << endl ; } weak_ptr<A> _ptra; }; int main () { shared_ptr <A> ptra (new A()) ; shared_ptr <B> ptrb (new B()) ; ptra->_ptrb = ptrb; ptrb->_ptra = ptra; cout << ptra.use_count() << endl ; cout << ptrb.use_count() << endl ; return 0 ; }
什么情况下需要自定义deleter
在管理的裸指针不是new
出来的裸指针时 需要自定义删除器,比如管理的指针为文件指针 或由malloc
获得的指针 等。
enable_shared_from_this
机制当class Widget
被share_ptr
管理,且在Widget
的成员函数里需要把当前类对象的智能指针作为参数传给其他函数时,就需要传递一个指向自身的share_ptr
。
而当使用shared_ptr
管理同一资源,调用shared_ptr
的构造函数和拷贝构造函数是不一样的(构造函数新创建一份堆上的引用计数资源,拷贝构造函数修改原来堆上的引用计数资源),它们虽然使得不同shared_ptr
指向同一资源,但管理引用计数资源的方式却不一样 。这就造成了不能直接传递shared_ptr<Widget>(this)
。因为这样会造成2
个非共享的share_ptr
指向同一个对象,引用计数资源相互独立导致对象被析构2
次。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 #include <memory> #include <iostream> class Widget { public : std ::shared_ptr <Widget> getptr () { return std ::shared_ptr <Widget>(this ); } ~Widget() { std ::cout << "Widget::~Widget() called" << std ::endl ; } }; int main () { std ::shared_ptr <Widget> p1 (new Widget; std ::shared_ptr <Widget> p2 = p1->getptr(); std ::cout << "p1.use_count() = " << p1.use_count() << std ::endl ; std ::cout << "p2.use_count() = " << p2.use_count() << std ::endl ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 #include <memory> #include <iostream> struct Widget : public std ::enable_shared_from_this<Widget> { public : std ::shared_ptr <Widget> getptr () { return shared_from_this(); } ~Widget() { std ::cout << "Widget::~Widget() called" << std ::endl ; } }; int main () { std ::shared_ptr <Widget> p1 (new Widget; std ::shared_ptr <Widget> p2 = p1->getptr(); std ::cout << "p1.use_count() = " << p1.use_count() << std ::endl ; std ::cout << "p2.use_count() = " << p2.use_count() << std ::endl ; }
实际使用场景:在异步调用中,存在一个保活机制,异步函数执行的时间点我们是无法确定的,然而异步函数可能会使用到异步调用之前就存在的变量。为了保证该变量在异步函数执期间一直有效,我们可以传递一个指向自身的share_ptr
给异步函数,这样在异步函数执行期间share_ptr
所管理的对象就不会析构,所使用的变量也会一直有效了(保活)。
enable_shared_from_this
是如何实现的enable_shared_from_this
类中包含一个作为观察者的成员变量,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 template <class T >class enable_shared_from_this { public : ... SetSharedPtr(shared_ptr <T>* sp) { if (weak_this_.expired()) wp = sp; } __shared_ptr<_Tp, _Lp> shared_from_this() { return __shared_ptr<_Tp, _Lp>(this ->_M_weak_this); } __shared_ptr<const _Tp, _Lp> shared_from_this() const { return __shared_ptr<const _Tp, _Lp>(this ->_M_weak_this); } private : mutable weak_ptr<_Tp> _M_weak_this; };
当一个类继承了enable_shared_from_this
类,就继承了_M_weak_this
这个成员变量。
使用shared_ptr<Widget>(new Widget())
第一次构造智能指针对象时,就会初始化一个作为观察者的弱智能指针_M_weak_this
指向Widget
对象资源。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class shared_ptr <T>{ public : explicit shared_ptr <T>(T* w) { SetSharedPtr(this , w); } void SetSharedPtr (shared_ptr <T>* sp, enable_shared_from_this<T>* w) { w->SetSharedPtr(sp); } void SetSharedPtr (...) { } };
通过shared_from_this()
方法代替shared_ptr
的普通构造函数来返回一个shared_ptr
对象,从而避免产生额外的引用计数资源对象。在shared_from_this()
函数中,是通过_M_weak_this
来构造并返回一个shared_ptr
对象的。
智能指针源码分析 unique_ptr
的声明包含两个模板参数,第一个参数_Tp
显然就是原生指针的类型。第二个模板参数_Dp
是一个deleter
,默认值为default_delete<_Tp>
。default_delete
是一个针对delete operator
的函数对象。
unique_ptr
内部用tuple<pointer, _Dp> _M_t;
变量保存数据,相当于原生指针和deleter
对象组成的一个pair
。
定义了构造函数和移动构造函数和移动赋值运算符,但是删除了拷贝构造函数和拷贝赋值运算符。
unqiue_ptr
还定义了两个很重要的函数:reset(pointer)
和release()
。reset(pointer)
的功能是用一个新指针替换原来的指针,而release()
则是是放弃原生指针的所有权。
到目前为止,unique_ptr
还不像个指针,因为还缺少两个方法:operator*
和operator->
。
下面是unique_ptr
的源码,有删减,但大体上不变。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 template <typename _Tp>struct default_delete { constexpr default_delete () noexcept = default ; void operator () (_Tp *__ptr) const { delete __ptr; } }; template <typename _Tp, typename _Dp>class __uniq_ptr_impl { template <typename _Up, typename _Ep, typename = void > struct _Ptr { using type = _Up *; }; template <typename _Up, typename _Ep> struct _Ptr <_Up, _Ep, __void_t<typename remove_reference<_Ep>::type::pointer>> { using type = typename remove_reference<_Ep>::type::pointer; }; public : using pointer = typename _Ptr<_Tp, _Dp>::type; __uniq_ptr_impl() = default ; __uniq_ptr_impl(pointer __p) : _M_t() { _M_ptr() = __p; } template <typename _Del> __uniq_ptr_impl(pointer __p, _Del &&__d) : _M_t(__p, std ::forward<_Del>(__d)) {} pointer &_M_ptr() { return std ::get<0 >(_M_t); } pointer _M_ptr() const { return std ::get<0 >(_M_t); } _Dp &_M_deleter() { return std ::get<1 >(_M_t); } const _Dp &_M_deleter() const { return std ::get<1 >(_M_t); } private : tuple<pointer, _Dp> _M_t; }; template <typename _Tp, typename _Dp = default_delete<_Tp>>class unique_ptr { __uniq_ptr_impl<_Tp, _Dp> _M_t; public : using pointer = typename __uniq_ptr_impl<_Tp, _Dp>::pointer; using element_type = _Tp; using deleter_type = _Dp; template <typename _Up = _Dp, typename = _DeleterConstraint<_Up>> constexpr unique_ptr () noexcept : _M_t() { } template <typename _Up = _Dp, typename = _DeleterConstraint<_Up>> explicit unique_ptr (pointer __p) noexcept : _M_t(__p) { } unique_ptr (pointer __p, typename remove_reference<deleter_type>::type &&__d) noexcept : _M_t(std ::move(__p), std ::move(__d)) { } unique_ptr (unique_ptr &&__u) noexcept : _M_t(__u.release(), std ::forward<deleter_type>(__u.get_deleter())) {} ~unique_ptr () noexcept { auto &__ptr = _M_t._M_ptr(); if (__ptr != nullptr ) get_deleter()(__ptr); __ptr = pointer(); } unique_ptr &operator =(unique_ptr &&__u) noexcept { reset(__u.release()); get_deleter() = std ::forward<deleter_type>(__u.get_deleter()); return *this ; } unique_ptr &operator =(nullptr_t ) noexcept { reset(); return *this ; } typename add_lvalue_reference<element_type>::type operator *() const { return *get(); } pointer operator ->() const noexcept { return get(); } pointer get () const noexcept { return _M_t._M_ptr(); } deleter_type &get_deleter () noexcept { return _M_t._M_deleter(); } const deleter_type &get_deleter () const noexcept { return _M_t._M_deleter(); } explicit operator bool () const noexcept { return get() == pointer() ? false : true ; } pointer release () noexcept { pointer __p = get(); _M_t._M_ptr() = pointer(); return __p; } void reset (pointer __p = pointer()) noexcept { using std ::swap; swap(_M_t._M_ptr(), __p); if (__p != pointer()) get_deleter()(__p); } void swap (unique_ptr &__u) noexcept { using std ::swap; swap(_M_t, __u._M_t); } unique_ptr (const unique_ptr &) = delete ; unique_ptr &operator =(const unique_ptr &) = delete ; };
下面是shared_ptr
的类图。
如图,shared_ptr
类几乎什么都没有做,它是继承了__shared_ptr
,__shared_ptr
内部有一个类型为__shared_count
类型的成员,__shared_count
内部有类型为_Sp_counted_base*
的成员。
_Sp_counted_base
才是整个shared_ptr
功能的核心,通过_Sp_counted_base
控制引用计数来管理指向的内存,由图可见_Sp_counted_base
内部不持有指向内存的指针,这里__shared_count
内部的_Sp_counted_base*
成员其实指向的是一个继承自_Sp_counted_base
的_Sp_counted_ptr
类型的派生类对象,_Sp_counted_ptr
类型内部持有指向内存的指针M_ptr
,这样__shared_count
内部就既可以控制引用计数,又可以在最后根据指向对象的指针释放托管内存。
注意的是:为什么_Sp_counted_ptr
和__shared_ptr
内部都有指向对象的指针。实际上它们可以是不同的类型(只要它们之间存在隐式转换),这是shared_ptr
的一大功能:
无需虚析构。假设Bar
是Foo
的基类,但是Bar
和Foo
都没有虚析构。
1 2 3 4 5 6 7 8 shared_ptr <Foo> sp1 (new Foo) ;shared_ptr <Bar> sp2 = sp1;sp1.reset();
此后sp2
仍然能安全地管理Foo
对象的生命期,并安全完整地释放Foo
,因为其_Sp_counted_ptr._M_ptr
记住了Foo
的实际类型。
shared_ptr<void>
可以指向并安全地管理(析构或防止析构)任何对象。
1 2 3 4 5 6 7 8 shared_ptr <Foo> sp1 (new Foo) ;shared_ptr <void > sp2 = sp1;sp1.reset();
此后sp2
仍然能安全地管理Foo
对象的生命期,并安全完整地释放Foo
,不会出现delete void*
的情况,因为delete
的是_Sp_counted_ptr._M_ptr
而不是__shared_ptr._M_ptr
。
多继承。假设Bar
是Foo
的多个基类之一,那么:
1 2 3 4 5 6 7 shared_ptr <Foo> sp1 (new Foo) ;shared_ptr <Bar> sp2 = sp1;sp1.reset();
但是sp2
仍然能安全地管理Foo
对象的生命期,并安全完整地释放Foo
,因为delete
的不是Bar*
,而是原来的 Foo*
。换句话说,sp1._M_ptr
和sp2._M_ptr
可能具有不同的值(当然它们的类型也不同)。
这里称_M_pi
为管理对象,它内部的_M_ptr
为托管对象 ,管理同一块托管对象的多个shared_ptr
内部共用一个管理对象,,这里的多个shared_ptr
可能是通过第一个shared_ptr
拷贝或者移动而来,管理对象内部有两个成员变量_M_use_count
和_M_weak_count
。
_M_use_count
表示托管对象的引用计数,控制托管对象什么时候析构和释放,当引用计数为0
时调用托管对象的析构函数且释放内存。
_M_weak_count
表示管理对象的引用计数,它的初始值比_M_use_count
大1
,管理对象也是一个内存指针,这块指针是初始化第一个shared_ptr
时new
出来的 ,到最后也需要delete
,所以使用_M_weak_count
来控制管理对象什么时候析构,当weak_ptr
析构时时,管理对象的引用计数_M_weak_count
就会减1
,当_M_weak_count
为0
时,管理对象_M_pi
就会析构且释放内存。
_M_use_count
是如何加减的
M use_count表示托管对象的引用计数,即当shared_ptr
拷贝时会增加,当shared_ptr
析构时会减少,看精简代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 template <typename _Yp>__shared_ptr(const __shared_ptr<_Yp, _Lp>& __r, element_type* __p) noexcept : _M_ptr(__p), _M_refcount(__r._M_refcount) { } __shared_count(const __shared_count& __r) noexcept : _M_pi(__r._M_pi) { if (_M_pi != 0 ) _M_pi->_M_add_ref_copy(); } template <>inline void _Sp_counted_base<_S_single>::_M_add_ref_copy(){ ++_M_use_count; }
shared_ptr
拷贝时,内部__shared_count
类型的_M_refcount
会进行拷贝,__shared_count
的拷贝构造函数会调用_M_add_ref_copy()
方法,_M_add_ref_copy()
方法中会将_M_use_count
加1
。
这里再看下shared_ptr
的赋值构造函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 template <typename _Yp>_Assignable<const shared_ptr <_Yp>&> operator =(const shared_ptr <_Yp>& __r) noexcept { this ->__shared_ptr<_Tp>::operator =(__r); return *this ; } template <typename _Yp>_Assignable<_Yp> operator =(const __shared_ptr<_Yp, _Lp>& __r) noexcept { _M_ptr = __r._M_ptr; _M_refcount = __r._M_refcount; return *this ; } __shared_count& operator =(const __shared_count& __r) noexcept { _Sp_counted_base<_Lp>* __tmp = __r._M_pi; if (__tmp != _M_pi) { if (__tmp != 0 ) __tmp->_M_add_ref_copy(); if (_M_pi != 0 ) _M_pi->_M_release(); _M_pi = __tmp; } return *this ; }
从代码中可见,shared_ptr
的operator=
会调用__shared_ptr
的operator=
进而调用__shared_count
的operator=
,从这里可以看出管理同一块托管对象的shared_ptr
共用的同一个管理对象的指针。
_M_use_count
是如何减为0
的,可以猜想到shared_ptr
析构时会调用__shared_count
的析构函数,看精简代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 ~__shared_count() noexcept { if (_M_pi != nullptr ) _M_pi->_M_release(); } template <>inline void _Sp_counted_base<_S_single>::_M_release() noexcept { if (--_M_use_count == 0 ) { _M_dispose(); if (--_M_weak_count == 0 ) _M_destroy(); } } virtual void _M_dispose() noexcept { delete _M_ptr; }
在shared_ptr
生命周期结束析构时会将引用计数减1,如果引用引用计数为0
,会调用_M_dispose()
函数进而释放托管对象内存。
_M_weak_count
是如何加减的
上面的代码中可以看见_M_weak_count
为0
时,会调用_M_destroy()
函数,这里看看_M_weak_count
是如何加减的。管理对象初始化时_M_weak_count
的初始值为1
1 _Sp_counted_base() noexcept : _M_use_count(1 ), _M_weak_count(1 ) {}
注意当shared_ptr
拷贝或者移动时_M_weak_count
是不会增加的,它表示的是管理对象的计数,只有当__M_use_count
为0时_M_weak_count
才会减1
,除此之外_M_weak_count
的数值是由weak_ptr
控制的。
由上面类图可以看见weak_ptr
内部其实和shared_ptr
内部持有的是同一个管理对象指针,即_Sp_counted_base
的指针,当weak_ptr
拷贝析构时候,_Sp_counted_base
内部的_M_weak_count
会相应加减。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 __weak_count(const __weak_count& __r) noexcept : _M_pi(__r._M_pi) { if (_M_pi != nullptr ) _M_pi->_M_weak_add_ref(); } template <>inline void _Sp_counted_base<_S_single>::_M_weak_add_ref() noexcept { ++_M_weak_count; } ~__weak_count() noexcept { if (_M_pi != nullptr ) _M_pi->_M_weak_release(); } template <>inline void _Sp_counted_base<_S_single>::_M_weak_release() noexcept { if (--_M_weak_count == 0 ) _M_destroy(); } virtual void _M_destroy() noexcept { delete this ; }
从代码中可以看出,weak_ptr
拷贝时_M_weak_count
加1
,析构时_M_weak_count
减1
,当_M_weak_count
为0
时,表示不再需要管理对象来控制托管对象,调用_M_destroy()
的delete this
来释放管理对象内存。
weak_ptr
的expired()
和lock()
做了什么
1 2 3 4 bool expired () const noexcept { return _M_refcount._M_get_use_count() == 0 ; }
weak_ptr
的expired()
函数只是看了托管对象的引用计数是否为0
,为0
返回true
。
1 2 3 4 5 6 7 8 9 10 __shared_ptr<_Tp, _Lp> lock() const noexcept { return __shared_ptr<element_type, _Lp>(*this , std ::nothrow); } __shared_ptr(const __weak_ptr<_Tp, _Lp>& __r, std ::nothrow_t ) : _M_refcount(__r._M_refcount, std ::nothrow) { _M_ptr = _M_refcount._M_get_use_count() ? __r._M_ptr : nullptr ; }
weak_ptr
的lock()
函数是打算返回一个shared_ptr
对象来延长托管对象的生命周期,这里返回后需要判断返回值是否为nullptr
。
shared_from_this()
是如何实现的
精简代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class enable_shared_from_this { __shared_ptr<_Tp, _Lp> shared_from_this() { return __shared_ptr<_Tp, _Lp>(this ->_M_weak_this); } __shared_ptr<const _Tp, _Lp> shared_from_this() const { return __shared_ptr<const _Tp, _Lp>(this ->_M_weak_this); } mutable weak_ptr<_Tp> _M_weak_this; };
使用shared_from_this()
的类需要继承enable_shared_from_this
类,enable_shared_from_this
类中持有一个类型为weak_ptr
的成员_M_weak_this
,调用shared_from_this()
就是将内部持有的weak_ptr
转成了shared_ptr
。
为什么建议使用make_shared()
函数 我们已经看到了,shared_ptr
内部维护了两个指针,如果你直接调用构造函数,就想这样:
1 2 class Foo ;auto sp = shared_ptr <Widget>(new Foo());
这里实际分配了两次内存,第一次是调用new Foo()
的时候,第二次则是在shared_ptr
构造函数的内部构造_Sp_counted_base
的时候。分配内存是很昂贵的操作,所以标准库提供了make_shared()
函数,让你一次分配全部所需的内存:
1 2 3 4 5 6 7 8 template <typename _Tp, _Lock_policy _Lp, typename ... _Args>inline __shared_ptr<_Tp, _Lp>__make_shared(_Args&&... __args) { typedef typename std ::remove_const<_Tp>::type _Tp_nc; return std ::__allocate_shared<_Tp, _Lp> (std ::allocator<_Tp_nc>(), std ::forward<_Args>(__args)...); }
现在用make_shared()
的话,可以一次分配一块足够大的内存,供Foo
和_Sp_counted_base
对象容身。
智能指针的线程安全性分析
同一个的shared_ptr
对象可以被多个线程同时读取(只读)
对同一个对象的并发读,显然是线程安全的。
不同的shared_ptr
对象可以被多个线程同时读写
如果这不同的shared_ptr
对象管理的是不同的对象资源,显然并发读写是线程安全的。
若它们指向的是同一个对象,这需要它们共同维护一份在堆上的引用计数资源。而shared_ptr
对象对于这份引用计数资源的读写使用了原子性操作,因此,也是线程安全的。
容易出现问题的是,从shared_ptr
构造weak_ptr
或者从weak_ptr
构造shared_ptr
的情况。
从shared_ptr
构造weak_ptr
由于weak_ptr
的构造过程中并不涉及引用计数资源的改变(实际上不改变的是shared_ptr
对象计数变量_M_use_count_
,只是改变了weak_ptr
对象计数变量_M_weak_count
),和前面的分析一样,使用了原子操作,也是线程安全的。
从weak_ptr
构造shared_ptr
而这一过程,会改变引用计数资源(增加shared_ptr
对象计数变量_M_use_count
),但是实际上也实现了线程安全的修改。可以去参考源码的实现。
同一个shared_ptr
对象不可以被多个线程同时读写(有写)
shared_ptr<Foo>
包含两个成员,一个是指向Foo
的指针_M_ptr
,另一个是_Sp_counted_base*
指针,指向堆上的 _Sp_counted_base
对象。因为这两个数据成员读写操作不是原子化,所以使用多个线程读写同一个shared_ptr
对象需要加锁,也就是说不是线程安全的。
自己动手实现一个基本的shared_ptr
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 #include <iostream> #include <functional> template <class T >class Deleter { public : using DefaultFunc = std ::function<void (T *)>; Deleter() : func_(std ::bind(&Deleter::defaultFunc, this , std ::placeholders::_1)) { std ::cout << "Deleter::Deleter()" << std ::endl ; } template <class D > Deleter (D func ) : func_(func) { std ::cout << "Deleter::Deleter(D func)" << std ::endl ; } void operator () (T *p) { func_(p); } private : DefaultFunc func_; void defaultFunc (T *p) { std ::cout << "Deleter::defaultFunc" << std ::endl ; delete p; } }; template <class T >class Ref_count_base { private : T *ptr_; int count_; Deleter<T> deleter_; public : Ref_count_base(T *p) : ptr_(p), count_(1 ) { std ::cout << "Ref_count_base::Ref_count_base(T *p)" << std ::endl ; } template <class D > Ref_count_base (T *p , D deleter ) : ptr_(p), count_(1 ), deleter_(deleter) { std ::cout << "Ref_count_base::Ref_count_base(T *p, D deleter)" << std ::endl ; } ~Ref_count_base() { std ::cout << "Ref_count_base::~Ref_count_base()" << std ::endl ; deleter_(ptr_); } int increase () { return count_++; } int reduce () { return --count_; } int getCount () { return count_; } }; template <class T >class Shared_ptr { private : T *ptr_; Ref_count_base<T> *ref_count_; public : Shared_ptr() : ptr_(nullptr ), ref_count_(nullptr ) { std ::cout << "Shared_ptr::Shared_ptr()" << std ::endl ; } explicit Shared_ptr (std ::nullptr_t ) { std ::cout << "Shared_ptr::Shared_ptr(std::nullptr_t)" << std ::endl ; Shared_ptr<T> temp; swap(temp); } explicit Shared_ptr(T *p) : ptr_(p), ref_count_(new Ref_count_base<T>(p)) { std ::cout << "Shared_ptr::Shared_ptr(T *p)" << std ::endl ; } template <class D > explicit Shared_ptr (T *p , D deleter ) : ptr_(p), ref_count_(new Ref_count_base<T>(p, deleter)) { std ::cout << "Shared_ptr::Shared_ptr(T *p, D deleter)" << std ::endl ; } ~Shared_ptr() { std ::cout << "Shared_ptr::~Shared_ptr()" << std ::endl ; if (ref_count_ && ref_count_->reduce() == 0 ) delete ref_count_; } Shared_ptr(const Shared_ptr<T> &sp) : ptr_(sp.ptr_), ref_count_(sp.ref_count_) { std ::cout << "Shared_ptr::Shared_ptr(const Shared_ptr<T> &sp)" << std ::endl ; if (ref_count_) ref_count_->increase(); } Shared_ptr &operator =(const Shared_ptr<T> &sp) { std ::cout << "Shared_ptr::operator=(const Shared_ptr<T> &sp)" << std ::endl ; Shared_ptr<T> temp (sp) ; swap(temp); return *this ; } Shared_ptr &operator =(std ::nullptr_t ) { std ::cout << "Shared_ptr::operator=(std::nullptr_t)" << std ::endl ; Shared_ptr<T> temp; swap(temp); return *this ; } T &operator *() { if (ptr_) return *ptr_; } T *operator ->() { if (ptr_) return ptr_; } T &operator [](ptrdiff_t i) { return ptr_[i]; } bool operator ==(Shared_ptr<T> &rhs) const { return ptr_ == rhs.ptr_; } operator bool () const { return ptr_ != nullptr ; } T *get () const { return ptr_; } int use_count () const { if (ref_count_) return ref_count_->getCount(); return 0 ; } void swap (Shared_ptr<T> &sp) { std ::cout << "Shared_ptr::swap(Shared_ptr<T> &sp)" << std ::endl ; using std ::swap; swap(ptr_, sp.ptr_); swap(ref_count_, sp.ref_count_); } }; template <class T >void swap (Shared_ptr <T> &lhs , Shared_ptr <T> &rhs ){ lhs.swap(rhs); } class Point { public : explicit Point (int i = 0 ) : val (i) { std ::cout << "Point(int i = 0)" << std ::endl ; } ~Point() { std ::cout << "~Point()" << std ::endl ; } Point &operator =(int i) { std ::cout << "Point::operator=(int i)" << std ::endl ; val = i; return *this ; } int getVal () const { return val; } private : int val; }; class myDeleter1 { public : void operator () (Point *p) { delete [] p; } }; void myDeleter2 (Point *p) { std ::cout << "myDeleter2(Point *p)" << std ::endl ; delete [] p; } auto myDeleter3 = [](Point *p) { delete [] p; };int main () { Shared_ptr<Point> p1 (new Point[2 ], myDeleter3) ; std ::cout << p1[0 ].getVal() << std ::endl ; p1[0 ] = 10 ; std ::cout << p1[0 ].getVal() << std ::endl ; std ::cout << "p1.use_count: " << p1.use_count() << std ::endl ; Shared_ptr<Point> p2 = p1; Shared_ptr<Point> p3; p3 = p2; std ::cout << "p1.use_count: " << p1.use_count() << std ::endl ; std ::cout << "p2.use_count: " << p2.use_count() << std ::endl ; std ::cout << "p3.use_count: " << p3.use_count() << std ::endl ; if (p1 == p2) std ::cout << "p1 == p2" << std ::endl ; else std ::cout << "p1 != p2" << std ::endl ; if (p1) std ::cout << "p1 为真" << std ::endl ; p1 = nullptr ; if (!p1) std ::cout << "p1 为假" << std ::endl ; std ::cout << "p1.use_count: " << p1.use_count() << std ::endl ; std ::cout << "p2.use_count: " << p2.use_count() << std ::endl ; std ::cout << "p3.use_count: " << p3.use_count() << std ::endl ; return 0 ; } Point::Point(int i = 0 ) Point::Point(int i = 0 ) Deleter::Deleter(D func) Ref_count_base::Ref_count_base(T *p, D deleter) Shared_ptr::Shared_ptr(T *p, D deleter) 0 Point::operator =(int i) 10 p1.use_count: 1 Shared_ptr::Shared_ptr(const Shared_ptr<T> &sp) Shared_ptr::Shared_ptr() Shared_ptr::operator =(const Shared_ptr<T> &sp) Shared_ptr::Shared_ptr(const Shared_ptr<T> &sp) Shared_ptr::swap(Shared_ptr<T> &sp) Shared_ptr::~Shared_ptr() p1.use_count: 3 p2.use_count: 3 p3.use_count: 3 p1 == p2 p1 为真 Shared_ptr::operator =(std ::nullptr_t ) Shared_ptr::Shared_ptr() Shared_ptr::swap(Shared_ptr<T> &sp) Shared_ptr::~Shared_ptr() p1 为假 p1.use_count: 0 p2.use_count: 2 p3.use_count: 2 Shared_ptr::~Shared_ptr() Shared_ptr::~Shared_ptr() Ref_count_base::~Ref_count_base() Point::~Point() Point::~Point() Shared_ptr::~Shared_ptr()
智能指针使用注意事项
尽量用make_shared/make_unique
std::shared_ptr
在实现的时候使用的ref count
技术,因此内部会有一个计数器(控制块,用来管理数据)和一个指针,指向数据。因此在执行std::shared_ptr<Widget> p2(new Widget)
的时候,首先会申请数据的内存,然后申请内控制块,因此是两次内存申请,而std::make_shared<Widget>()
则是只执行一次内存申请,将数据和控制块的申请放到一起。
不要使用相同的内置指针来初始化(或者reset
)多个智能指针
不要delete get()
返回的指针
不要用get()
初始化或reset
另一个智能指针
智能指针管理的资源它只会默认删除new
分配的内存,如果不是new
分配的则要传递给其一个删除器
以下代码试图将malloc
产生的动态内存交给shared_ptr
管理,显然是有问题的,所以我们需要自定义删除器传递给shared_ptr
。
1 2 3 4 5 6 7 8 9 10 { int * pi = (int *)malloc (4 * sizeof (int )); shared_ptr <int > sp (pi) ; } { int * pi = (int *)malloc (4 * sizeof (int )); shared_ptr <int > sp (pi, [](int * p){ free (p); }) ; }
不要把一个原生指针给多个shared_ptr
或者多个unique_ptr
管理
在使用原生指针对智能指针初始化的时候,智能指针对象都视原生指针为自己管理的资源。换句话意思就说:初始化多个智能指针之后,这些智能指针都担负起释放内存的作用。那么就会导致该原生指针会被释放多次!!
1 2 3 4 int * ptr = new int ;shared_ptr <int > p1 (ptr) ;shared_ptr <int > p2 (ptr) ;