Smart Pointers原始碼 + CppCon筆記Back to Basics: Smart Pointers
智慧指標是基於RAII的理念設計的一個資源的封裝,能讓類不直接管理資源,從而減少錯誤發生(忘記釋放)。
1.unique_ptr
智慧指標的理解要和資源的所有權相聯絡。unique_ptr代表的是獨佔的所有權(exclusive ownership),所封裝的指標不能與其他共享,否則double free。
1.1.unique_ptr實現
unique_ptr主要的組成是一個所管理的指標,一個可以自定義的deleter, 而且move only(畢竟不能共享指標)。是一個處理low-level和非RAII的好方法。
原理:
unique_ptr的宣告
template <typename _Tp, typename _Tp_Deleter = default_delete<_Tp> > class unique_ptr;
unique_ptr中禁止拷貝
// Disable copy from lvalue. unique_ptr(const unique_ptr&) = delete; template<typename _Up, typename _Up_Deleter> unique_ptr(const unique_ptr<_Up, _Up_Deleter>&) = delete; unique_ptr& operator=(const unique_ptr&) = delete; template<typename _Up, typename _Up_Deleter> unique_ptr& operator=(const unique_ptr<_Up, _Up_Deleter>&) = delete;
deleter
template<typename _Tp> struct default_delete<_Tp> { default_delete() {} template<typename _Up> default_delete(const default_delete<_Up>&) { } void operator()(_Tp* __ptr) const { static_assert(sizeof(_Tp)>0, "can't delete pointer to incomplete type"); delete __ptr; } };
此外還有一套針對array的定義,這裡使用了template的specialization技法,輸入的是array的話會呼叫這個類。https://stackoverflow.com/questions/19923353/multiple-typename-arguments-in-c-template。
template<typename _Tp, typename _Tp_Deleter>
class unique_ptr<_Tp[], _Tp_Deleter>
移動構造,release能獲得unique_ptr所管理指標,並釋放所有權
// Move constructors.
unique_ptr(unique_ptr&& __u)
: _M_t(__u.release(), std::forward<deleter_type>(__u.get_deleter())) { }
__tuple_type _M -> typedef std::tuple<_Tp*, _Tp_Deleter> __tuple_type;
pointer get() const
{ return std::get<0>(_M_t); }
typename std::add_lvalue_reference<deleter_type>::type
get_deleter()
{ return std::get<1>(_M_t); }
pointer release()
{
pointer __p = get();
std::get<0>(_M_t) = 0; // nullify 所管理指標
return __p;
}
解構函式
~unique_ptr() { reset(); }
// 替換所管理的指標,如果沒有引數,就是與一個空指標替換
reset(pointer __p = pointer()) {
if (__p != get()) { // unique_ptr為空時不會釋放
get_deleter()(get()); // 等同deleter(ptr), 使用deleter釋放ptr
std::get<0>(_M_t) = __p;
}
}
1.2.make_unique實現
unique_ptr
forward + variadic template(並不是pack expression,所以c++14就有make_unique)
/// std::make_unique for single objects
template<typename _Tp, typename... _Args>
inline typename _MakeUniq<_Tp>::__single_object
make_unique(_Args&&... __args)
{ return unique_ptr<_Tp>(new _Tp(std::forward<_Args>(__args)...)); }
1.3.unique_ptr使用
所有權轉換的例子
// a function consuming a unique_ptr can take it by value or by rvalue reference
std::unique_ptr<D> pass_through(std::unique_ptr<D> p)
{
p->bar();
return p;
}
int main() {
auto p = std::make_unique<D>(); // p is a unique_ptr that owns a D
auto q = pass_through(std::move(p));
assert(!p); // now p owns nothing and holds a null pointer
q->bar(); // and q owns the D object
}
1.3.1.自定義deleter
2.shared_ptr
一種管理可共享資源的智慧指標,這就是說有多個shared_ptr指向同一個指標,並存在一個計數,當計數為0時後才釋放資源。
2.0.shared_ptr和weak_ptr
解決成環問題所以引入了weak_ptr,只增加weak_count不增加use_count
https://stackoverflow.com/questions/4984381/shared-ptr-and-weak-ptr-differences
class A { shared_ptr<B> b; ... };
class B { shared_ptr<A> a; ... };
shared_ptr<A> x(new A); // +1
x->b = new B;
x->b->a = x; // +1
// Ref count of 'x' is 2.
// Ref count of 'x->b' is 1.
// When 'x' leaves the scope, there will be a memory leak:
// 2 is decremented to 1, and so both ref counts will be 1.
// (Memory is deallocated only when ref count drops to 0)
2.1.shared_ptr實現
原理
shared_ptr包含了資源指標和一個計數的內部類,在計數類__shared_count中,包含儲存引用計數,弱引用計數還有一個指向deleter和資源指標的_Sp_counted_base。_Sp_counted_ptr是對_Sp_counted_base的封裝。
2.1.1.shared_ptr和__shared_ptr
shared_ptr只是__shared_ptr的簡單封裝,而__shared_ptr包含了資源指標和一個計數類__shared_count。 __shared_count負責資源計數,釋放幷包含deleter。
// __shared_ptr的成員
element_type* _M_ptr; // Contained pointer.
__shared_count<_Lp> _M_refcount; // Reference counter.擁有deleter和資源指標
shared_ptr只是__shared_ptr的簡單封裝
// Construct a shared_ptr that owns the pointer __p.
template<typename _Tp1>
explicit shared_ptr(_Tp1* __p)
: __shared_ptr<_Tp>(__p) { }
__shared_ptr包含資源的指標和一個棧上的計數物件__shared_count,__shared_ptr不負責_M_ptr的釋放,這些活是由__shared_count來負責的。
// __shared_ptr的宣告
template<typename _Tp, _Lock_policy _Lp>
class __shared_ptr
: public __shared_ptr_access<_Tp, _Lp> {/* 省略*/}
// __shared_ptr 的成員變數
element_type* _M_ptr; // Contained pointer.
__shared_count<_Lp> _M_refcount; // Reference counter.擁有deleter和資源指標
// __shared_ptr 的解構函式
~__shared_ptr() = default;
__shared_ptr的建構函式之一
template<typename _Yp, typename _Deleter, typename = _SafeConv<_Yp>>
__shared_ptr(_Yp* __p, _Deleter __d)
: _M_ptr(__p), _M_refcount(__p, std::move(__d)) // _M_refcount擁有deleter和資源指標
{
static_assert(__is_invocable<_Deleter&, _Yp*&>::value,
"deleter expression d(p) is well-formed");
_M_enable_shared_from_this_with(__p);
}
2.1.2.__shared_ptr_access
__shared_ptr的父類__shared_ptr_access主要負責過載解引用和->操作符。
using element_type = _Tp;
element_type&
operator*() const noexcept
{
__glibcxx_assert(_M_get() != nullptr); // 會對是否為空指標進行判斷。
return *_M_get();
}
2.1.3.__shared_count
__shared_count是shared_ptr的計數模組,也是真正管理釋放的部分(或者說_Sp_counted_base更準確),且多個shared_ptr例項共用一個__shared_count(__shared_count中賦值建構函式可以看到_Sp_counted_base<_Lp>* __tmp = __r._M_pi;)。不過切忌分別建立兩個獨立的shared_ptr指向同個資源,control block(計數模組)是不一樣的,還是會double delete。
成員變數
_Sp_counted_base<_Lp>* _M_pi;
__shared_count的建構函式
template<_Lock_policy _Lp>
class __shared_count{/* 省略*/}
// _Sp_counted_base<_Lp>* _M_pi;
template<typename _Ptr>
explicit __shared_count(_Ptr __p) : _M_pi(0) {
__try {
// _Sp_counted_base<_Lp>* _M_pi = new _Sp_counted_ptr<_Ptr, _Lp>(__p);
_M_pi = new _Sp_counted_ptr<_Ptr, _Lp>(__p);
}
__catch(...) {
delete __p;
__throw_exception_again;
}
}
__shared_count的解構函式
~__shared_count() noexcept
{
if (_M_pi != nullptr)
_M_pi->_M_release(); // use_count--, 如果use_count等於1,清理資源。
}
針對_M_pi_, 其可能是Sp_counted_deleter或者_Sp_counted_ptr的多型表現。
Sp_counted_deleter在_Sp_counted_ptr的基礎外多了deleter和allocator
// 自定義deleter時的__shared_count的建構函式
// ::new (__mem) _Sp_cd_type(__p, std::move(__d), std::move(__a)); // placement new, 在已分配的記憶體__mem上再分配
// _Sp_counted_base<_Lp>* _M_pi = __mem;
template<typename _Ptr, typename _Deleter, typename _Alloc>
__shared_count(_Ptr __p, _Deleter __d, _Alloc __a) : _M_pi(0)
{
typedef _Sp_counted_deleter<_Ptr, _Deleter, _Alloc, _Lp> _Sp_cd_type;
__try
{
typename _Sp_cd_type::__allocator_type __a2(__a);
auto __guard = std::__allocate_guarded(__a2);
_Sp_cd_type* __mem = __guard.get();
::new (__mem) _Sp_cd_type(__p, std::move(__d), std::move(__a));
_M_pi = __mem;
__guard = nullptr;
}
__catch(...)
{
__d(__p); // Call _Deleter on __p.
__throw_exception_again;
}
}
2.1.4._Sp_counted_base
_Sp_counted_base的成員函式
_Atomic_word _M_use_count; // #shared
_Atomic_word _M_weak_count; // #weak + (#shared != 0)
_Sp_counted_base的建構函式和解構函式
template<_Lock_policy _Lp = __default_lock_policy>
class _Sp_counted_base
: public _Mutex_base<_Lp> {/* 省略*/}
// _Sp_counted_base的建構函式
_Sp_counted_base() noexcept
: _M_use_count(1), _M_weak_count(1) { }
// _Sp_counted_base的解構函式
virtual
~_Sp_counted_base() noexcept
{ }
_Sp_counted_base的資源釋放函式
// Called when _M_use_count drops to zero, to release the resources
// managed by *this.
virtual void
_M_dispose() noexcept = 0;
void
_M_release() noexcept
{
// Be race-detector-friendly. For more info see bits/c++config.
_GLIBCXX_SYNCHRONIZATION_HAPPENS_BEFORE(&_M_use_count);
if (__gnu_cxx::__exchange_and_add_dispatch(&_M_use_count, -1) == 1)
{
_GLIBCXX_SYNCHRONIZATION_HAPPENS_AFTER(&_M_use_count);
_M_dispose();
// There must be a memory barrier between dispose() and destroy()
// to ensure that the effects of dispose() are observed in the
// thread that runs destroy().
// See http://gcc.gnu.org/ml/libstdc++/2005-11/msg00136.html
if (_Mutex_base<_Lp>::_S_need_barriers)
{
__atomic_thread_fence (__ATOMIC_ACQ_REL);
}
// Be race-detector-friendly. For more info see bits/c++config.
_GLIBCXX_SYNCHRONIZATION_HAPPENS_BEFORE(&_M_weak_count);
if (__gnu_cxx::__exchange_and_add_dispatch(&_M_weak_count,
-1) == 1)
{
_GLIBCXX_SYNCHRONIZATION_HAPPENS_AFTER(&_M_weak_count);
_M_destroy();
}
}
}
// _Sp_counted_base喜加一
void
_M_add_ref_copy()
{ __gnu_cxx::__atomic_add_dispatch(&_M_use_count, 1); }
// __shared_count的複製 -> 使用同一個_Sp_counted_base,並讓其喜加一
__shared_count(const __shared_count& __r) noexcept
: _M_pi(__r._M_pi)
{
if (_M_pi != 0)
_M_pi->_M_add_ref_copy();
}
_Sp_counted_ptr 和 _Sp_counted_deleter
// Counted ptr with no deleter or allocator support
template<typename _Ptr, _Lock_policy _Lp>
class _Sp_counted_ptr final : public _Sp_counted_base<_Lp> {/* 省略*/}
// _Sp_counted_ptr的釋放函式
virtual void
_M_dispose() noexcept
{ delete _M_ptr; }
virtual void
_M_destroy() noexcept
{ delete this; }
// Support for custom deleter and/or allocator
template<typename _Ptr, typename _Deleter, typename _Alloc, _Lock_policy _Lp>
class _Sp_counted_deleter final : public _Sp_counted_base<_Lp> {/* 省略*/}
_Sp_counted_deleter的釋放
// _Impl類是利用EBO的一種實現,沒看明白
~_Sp_counted_deleter() noexcept { }
virtual void
_M_dispose() noexcept
{ _M_impl._M_del()(_M_impl._M_ptr); }
virtual void
_M_destroy() noexcept
{
__allocator_type __a(_M_impl._M_alloc());
__allocated_ptr<__allocator_type> __guard_ptr{ __a, this };
this->~_Sp_counted_deleter();
}
2.2.Aliasing constructor
// Aliasing constructor
/**
* @brief Constructs a %shared_ptr instance that stores @a __p
* and shares ownership with @a __r.
* @param __r A %shared_ptr.
* @param __p A pointer that will remain valid while @a *__r is valid.
* @post get() == __p && use_count() == __r.use_count()
*
* This can be used to construct a @c shared_ptr to a sub-object
* of an object managed by an existing @c shared_ptr.
*
* @code
* shared_ptr< pair<int,int> > pii(new pair<int,int>());
* shared_ptr<int> pi(pii, &pii->first);
* assert(pii.use_count() == 2);
* @endcode
*/
template<typename _Yp>
shared_ptr(const shared_ptr<_Yp>& __r, element_type* __p) noexcept
: __shared_ptr<_Tp>(__r, __p) { }
// template<typename _Tp, _Lock_policy _Lp = __default_lock_policy> class __shared_ptr;
template<typename _Yp>
__shared_ptr(const __shared_ptr<_Yp, _Lp>& __r,
element_type* __p) noexcept
_M_ptr(__p), _M_refcount(__r._M_refcount) // never throws
}
一個shared_ptr的特殊用法,指向類的成員,並增加類的引用計數,來增加使用成員類時的生命週期
struct Bar {
// some data that we want to point to
};
struct Foo {
Bar bar;
};
shared_ptr<Foo> f = make_shared<Foo>(some, args, here);
shared_ptr<Bar> specific_data(f, &f->bar);
// ref count of the object pointed to by f is 2
f.reset();
// the Foo still exists (ref cnt == 1)
// so our Bar pointer is still valid, and we can use it for stuff
some_func_that_takes_bar(specific_data);
2.3.進行使用make_shared
- new和delete對應
- cache locality
用同個malloc申請記憶體給control block(計數模組)和資源 -> 在相鄰地址
3.weak_ptr
weak_ptr不能解引用,所以可以理解成一個shared_ptr的門票,如果你由一個weak_ptr你也就能獲得相應的shared_ptr(使用門票來使用)。
3.1.weak_ptr使用
3.1.1.weak_ptr由shared_ptr獲得
shared_ptr<Thing> sp(new Thing);
weak_ptr<Thing> wp1(sp); // construct wp1 from a shared_ptr
weak_ptr<Thing> wp2; // an empty weak_ptr - points to nothing
wp2 = sp; // wp2 now points to the new Thing
weak_ptr<Thing> wp3 (wp2); // construct wp3 from a weak_ptr
weak_ptr<Thing> wp4
wp4 = wp2; // wp4 now points to the new Thing.
3.1.2.weak_ptr轉為shared_ptr
使用lock成員函式
shared_ptr<_Tp>
lock() const noexcept
{ return shared_ptr<_Tp>(*this, std::nothrow); }
std::nothrow是一個用於區分過載的東西,區別是如果weak_ptr已經expired了,會丟擲異常。
shared_ptr有對應的建構函式(throwing)
// throwing版本
// Now that __weak_count is defined we can define this constructor:
template<_Lock_policy _Lp>
inline
__shared_count<_Lp>::__shared_count(const __weak_count<_Lp>& __r)
: _M_pi(__r._M_pi)
{
if (_M_pi != nullptr)
_M_pi->_M_add_ref_lock();
else
__throw_bad_weak_ptr(); // 如果weak_ptr已經expired了,丟擲一個bad weak ptr異常
}
// no throwing版本
// Now that __weak_count is defined we can define this constructor:
template<_Lock_policy _Lp>
inline
__shared_count<_Lp>::
__shared_count(const __weak_count<_Lp>& __r, std::nothrow_t)
: _M_pi(__r._M_pi)
{
if (_M_pi != nullptr)
if (!_M_pi->_M_add_ref_lock_nothrow())
_M_pi = nullptr;
}
推薦使用lock,再判讀是否是空指標(是否expired)
3.1.3.expired和use_count
判斷weak_ptr所指物件是否expired,和多少shared_ptr物件指向被管理的資源物件。
bool expired() const noexcept
{ return _M_refcount._M_get_use_count() == 0; }
long use_count() const noexcept
{ return _M_refcount._M_get_use_count(); }
3.1.3.std::enable_shared_from_this
還有一個問題沒有解決,當一個物件T建立後,如果一個函式需要shared_ptr
同樣,這也是一種讓函式介入物件生命週期的方法。
/**
* @brief Base class allowing use of member function shared_from_this.
*/
template<typename _Tp>
class enable_shared_from_this {
/*省略*/
public:
mutable weak_ptr<_Tp> _M_weak_this;
shared_ptr<_Tp> shared_from_this()
{ return shared_ptr<_Tp>(this->_M_weak_this); }
}