shared_ptr实现
shared_ptr为c++告别裸指针,使更简单的对象管理提供了一个工具,下面就gcc4.9中shared_ptr的实现,以及与shared_ptr相关的几个概念到源码中寻找答案
这里只列出了部分源码
shared_ptr定义
shared_ptr.h
template<typename _Tp>
class shared_ptr : public __shared_ptr<_Tp>
{
}
shared_ptr只是一个壳子,提供了外界的调用接口,真正的实现都在__shared_ptr基类中
__shared_ptr<>
template<typename _Tp, _Lock_policy _Lp>
class __shared_ptr
{
_Tp* _M_ptr; // Contained pointer.
__shared_count<_Lp> _M_refcount; // Reference counter.
};
_Lock_policy为在count操作中使用锁的policy,分为无锁/mutex/atomic
__shared_count<>
template<_Lock_policy _Lp>
class __shared_count
{
private:
friend class __weak_count<_Lp>;
_Sp_counted_base<_Lp>* _M_pi;
};
构造函数:
template<typename _Ptr>
explicit
__shared_count(_Ptr __p) : _M_pi(0)
{
__try
{ // _Sp_counted_ptr继承自_Sp_counted_base<_Lp>
// 成员只有一个:_Ptr _M_ptr,这里要继承一下,是为了直接在counted里面去释放shared_ptr的数据成员
// 还要保留一份数据成员的指针,提供了_M_dispose方法来删除数据成员
// 两个count成员初始化为1
_M_pi = new _Sp_counted_ptr<_Ptr, _Lp>(__p);
}
__catch(...)
{
delete __p;
__throw_exception_again;
}
}
复制构造
__shared_count(const __shared_count& __r) noexcept
: _M_pi(__r._M_pi) // 指针浅拷贝
{
if (_M_pi != 0)
_M_pi->_M_add_ref_copy(); // _M_use_count+1
}
赋值构造:
__shared_count&
operator=(const __shared_count& __r) noexcept
{
_Sp_counted_base<_Lp>* __tmp = __r._M_pi; // 浅拷贝
if (__tmp != _M_pi) // 不相等才赋值,相等就直接返回了
{
if (__tmp != 0) // _M_use_count自增1
__tmp->_M_add_ref_copy();
if (_M_pi != 0) // 成员变量释放,并赋值入参值
_M_pi->_M_release();
_M_pi = __tmp;
}
return *this;
}
析构
~__shared_count() noexcept
{
if (_M_pi != nullptr)
// _M_use_count=1时,调用 _M_dispose()释放数据成员;_M_weak_count-1也等于0,执行_M_destroy()
_M_pi->_M_release();
}
可以看到,__shared_count在构造函数中令_M_use_count和_M_use_count都为1,而复制和赋值构造中只给_M_use_count加1, 并没有操作_M_use_count的增减
_Sp_counted_base<_Lp>
实现两个count的安全读写/add/release等,执行计数器的原子操作,以及对shared_ptr堆上资源的释放。
enum _Lock_policy { _S_single, _S_mutex, _S_atomic };
_Lock_policy在不同的情况下,用宏定义了默认policy为三个枚举之一
// 这里对lock policy做了默认特化
template<_Lock_policy _Lp = __default_lock_policy>
class _Sp_counted_base
: public _Mutex_base<_Lp>
{
public:
_Sp_counted_base() noexcept
: _M_use_count(1), _M_weak_count(1) { } // 构造后都为1
virtual
~_Sp_counted_base() noexcept
{ }
// Called when _M_use_count drops to zero, to release the
// resources managed by *this.
virtual void
_M_dispose() noexcept = 0; // 纯虚的,在子类中实现删除数据指针
// Called when _M_weak_count drops to zero.
virtual void _M_destroy() noexcept; // delete this
virtual void*
_M_get_deleter(const std::type_info&) noexcept = 0;
void _M_add_ref_copy(); // _M_use_count+1
void _M_add_ref_lock(); // _M_use_count为0抛异常,否则_M_use_count++
bool _M_add_ref_lock_nothrow(); // _M_use_count为0返回false,否则_M_use_count++返回true
void _M_release() noexcept; // _M_use_count=1时,调用 _M_dispose()释放数据成员;_M_weak_count-1也等于0,执行_M_destroy()
void _M_weak_add_ref() noexcept; // _M_weak_count + 1
void _M_weak_release() noexcept; // _M_weak_count-1如果等于0的话,那么执行_M_destroy()
long _M_get_use_count() const noexcept;// 获取_M_use_count的值
private:
_Sp_counted_base(_Sp_counted_base const&) = delete;
_Sp_counted_base& operator=(_Sp_counted_base const&) = delete; // 不允许复制/赋值
// typedef int _Atomic_word;
_Atomic_word _M_use_count; // #shared
_Atomic_word _M_weak_count; // #weak + (#shared != 0)
};
_M_use_count为共享计数
_M_weak_count为弱引用计数
函数实现
主要看部分构造函数实现
构造函数
template<typename _Tp1>
explicit __shared_ptr(_Tp1* __p)
: _M_ptr(__p), _M_refcount(__p)
{
// 一些合法性校验
__glibcxx_function_requires(_ConvertibleConcept<_Tp1*, _Tp*>)
static_assert( !is_void<_Tp1>::value, "incomplete type" );
static_assert( sizeof(_Tp1) > 0, "incomplete type" );
// 这个函数调用,使用_M_refcount和__p为对象__p(继承了
// enable_shared_from_this)赋值
__enable_shared_from_this_helper(_M_refcount, __p, __p);
}
复制构造
template<typename _Tp1>
shared_ptr(const shared_ptr<_Tp1>& __r, _Tp* __p) noexcept
: __shared_ptr<_Tp>(__r, __p) { }
template<typename _Tp1>
__shared_ptr(const __shared_ptr<_Tp1, _Lp>& __r, _Tp* __p) noexcept
: _M_ptr(__p), _M_refcount(__r._M_refcount) // never throws
{ }
复制构造,直接使用入参给数据指针赋值,调用_M_refcount的复制构造函数就可以了
移动构造
template<typename _Tp1, typename = typename
std::enable_if<std::is_convertible<_Tp1*, _Tp*>::value>::type>
shared_ptr(shared_ptr<_Tp1>&& __r) noexcept
: __shared_ptr<_Tp>(std::move(__r)) { }
template<typename _Tp1, typename = typename
std::enable_if<std::is_convertible<_Tp1*, _Tp*>::value>::type>
__shared_ptr(__shared_ptr<_Tp1, _Lp>&& __r) noexcept
: _M_ptr(__r._M_ptr), _M_refcount()
{
_M_refcount._M_swap(__r._M_refcount);
__r._M_ptr = 0;
}
右值移动构造函数
- 入参的count与this交换(为什么要交换呢?比构造代价小),入参数据指针=0
- this的count与入参的交换,数据指针赋值为入参的数据指针
- 数据指针浅拷贝
赋值构造
template<typename _Tp1>
shared_ptr&
operator=(const shared_ptr<_Tp1>& __r) noexcept
{
this->__shared_ptr<_Tp>::operator=(__r);
return *this;
}
template<typename _Tp1>
__shared_ptr&
operator=(const __shared_ptr<_Tp1, _Lp>& __r) noexcept
{
_M_ptr = __r._M_ptr;
// 释放_M_refcount老值,赋新值,并_M_use_count加1
_M_refcount = __r._M_refcount; // __shared_count::op= doesn't throw
return *this;
}
赋值构造函数:
- 有返回值,返回值为*this,类型为shared_ptr&
- 数据成员和count计数直接用=赋值,调用__shared_count的赋值构造函数
移动赋值构造
template<class _Tp1>
shared_ptr&
operator=(shared_ptr<_Tp1>&& __r) noexcept
{ // 可以通过this->__shared_ptr<_Tp>::调用吗?不是this->base吗?
this->__shared_ptr<_Tp>::operator=(std::move(__r));
return *this;
}
void swap(__shared_ptr<_Tp, _Lp>& __other) noexcept
{
std::swap(_M_ptr, __other._M_ptr);
_M_refcount._M_swap(__other._M_refcount);
} // 交换智能指针,每个成员依次交换
template<class _Tp1>
__shared_ptr&
operator=(__shared_ptr<_Tp1, _Lp>&& __r) noexcept
{
__shared_ptr(std::move(__r)).swap(*this);
return *this;
}
移动赋值构造函数实现:
- 调用移动构造函数使用入参构造一个临时__shared_ptr,然后交换临时变量和this
- 返回*this
析构函数
shared_ptr/__shared_ptr的析构函数都没有显示实现,也就是会调用默认析构函数
默认析构函数会做什么事情
- 释放_M_ptr栈上的空间(即指针本身),但是不会释放堆上的实际存储内容
- 调用__shared_count的析构函数释放_M_refcount内存
__shared_count部分已经分析过,调用__shared_count析构函数会调用__shared_count_base的_M_release()同时释放数据空间和count,规则为:
- _M_use_count减1,如果_M_use_count计数为0了,调用_M_dispose()(派生类的成员函数)释放数据成员
- _M_weak_count减1, 如果_M_weark_count也为0了,那么执行_M_destroy()函数释放__shared_count对象
weak_ptr
定义
weak_ptr是一种弱引用,它对shared_ptr所管理的对象存在非拥有性引用,两种应用场景参见cppreference
定义比较简单,其定义以及所有实现的方法:
template<typename _Tp>
class weak_ptr : public __weak_ptr<_Tp>
{
public:
constexpr weak_ptr() noexcept
: __weak_ptr<_Tp>() { }
template<typename _Tp1, typename = typename
std::enable_if<std::is_convertible<_Tp1*, _Tp*>::value>::type>
weak_ptr(const weak_ptr<_Tp1>& __r) noexcept
: __weak_ptr<_Tp>(__r) { }
template<typename _Tp1, typename = typename
std::enable_if<std::is_convertible<_Tp1*, _Tp*>::value>::type>
weak_ptr(const shared_ptr<_Tp1>& __r) noexcept
: __weak_ptr<_Tp>(__r) { }
template<typename _Tp1>
weak_ptr&
operator=(const weak_ptr<_Tp1>& __r) noexcept;
template<typename _Tp1>
weak_ptr&
operator=(const shared_ptr<_Tp1>& __r) noexcept;
shared_ptr<_Tp>
lock() const noexcept
{ return shared_ptr<_Tp>(*this, std::nothrow); }
};
其只定义了一些构造函数,以及lock()方法用于判断所引用的shared_ptr对象是否被释放
其中在构造函数,只能构造空的weak_ptr,以及使用weak_ptr/shared_ptr进行构造,而不能使用原始指针进行构造
template<typename _Tp, _Lock_policy _Lp>
class __weak_ptr
{
_Tp* _M_ptr; // Contained pointer.
__weak_count<_Lp> _M_refcount; // Reference counter.
};
template<typename _Tp1>
__weak_ptr&
operator=(const __shared_ptr<_Tp1, _Lp>& __r) noexcept
{
_M_ptr = __r._M_ptr;
_M_refcount = __r._M_refcount; // 调用__weak_count的赋值函数
return *this;
}
与__shared_ptr定义类似,只是计数器类型由__shared_count换成了__weak_count, __weak_count的定义如下,它的成员变量与__shared_count相同,但是在成员函数操作上,可以看到它只操作了_M_weak_count的增加/减少等,没有处理__M_use_count
template<_Lock_policy _Lp>
class __weak_count
{
public:
constexpr __weak_count() noexcept : _M_pi(0) { }
__weak_count(const __shared_count<_Lp>& __r) noexcept
: _M_pi(__r._M_pi) // 存在吗?
{
if (_M_pi != 0)
_M_pi->_M_weak_add_ref(); // 只加_M_weak_count
}
~__weak_count() noexcept
{
if (_M_pi != 0)
_M_pi->_M_weak_release(); //--_M_weak_count == 0, _M_destroy()(delete this)
}
__weak_count<_Lp>&
operator=(const __shared_count<_Lp>& __r) noexcept
{
_Sp_counted_base<_Lp>* __tmp = __r._M_pi; // 浅拷贝为入参
if (__tmp != 0)
__tmp->_M_weak_add_ref(); // _M_weak_count+1
if (_M_pi != 0)
_M_pi->_M_weak_release(); // 释放旧的
_M_pi = __tmp;
return *this;
}
。。。。。。
private:
friend class __shared_count<_Lp>;
_Sp_counted_base<_Lp>* _M_pi;
};
如上看起来,不考虑循环引用问题,weak_ptr似乎也可以作为智能指针使用,但是实际上是不可以的,原因在方法实现上:
- 没有提供指向原始指针的构造函数,所以只能指向shared_ptr,不能直接指向原始指针对象
- 没有定义*/->等操作符,不能直接操作对象
防止循环引用
众所周知,shared_ptr可能造成循环引用问题,那么weak_ptr是怎么解决循环引用问题的呢?看一个例子
class A {
public:
shared_ptr<B> ab;
// 这里可以加上构造/析构函数看下是否能够被调用
};
class B {
public:
shared_ptr<A> ba;
// 这里可以加上构造/析构函数看下是否能够被调用
};
shared_ptr<A> spa = make_shared<A>(); // 定义一个A对象
shared_ptr<B> spb = make_shared<B>(); // 定义一个A对象
spa.ab = spb; //shared_ptr计数成员_Sp_counted_base浅拷贝,use ref++
spb.ba = spa;
显然,由于A、B互相引用,都无法释放,造成内存泄漏(示例参考自blog)
怎样打破这种循环引用?cppreference的说法是
std::weak_ptr 的另一用法是打断 std::shared_ptr 所管理的对象组成的环状引用。若这种环被孤立(例如无指向环中的外部共享指针),则 shared_ptr 引用计数无法抵达零,而内存被泄露。能令环中的指针之一为弱指针以避免此情况。
具体修改方法:
将class A的成员ab(或者class B的成员ba)的类型由shared_ptr修改为weak_ptr,这两种改法有什么不同呢?就是析构顺序会有差异,拥有weak_ptr的对象后被释放
再从源码实现来看原因,为什么类成员类型修改为weak_ptr之后,class A/B的析构函数就可以调用成功了呢?假设把B.ba改为weak_ptr类型
- make_shared之后,spa/spb的_M_use_count=1, _M_weak_count=1
- 互相赋值语句发生后
- A.ab成员执行赋值语句:浅拷贝spb的计数指针_M_pi后,给_M_use_count加1;所以A.ab与spb的计数值:_M_use_count为2,_M_weak_count=1;
- B.ba(weak)成员执行赋值语句:调用weak_ptr(weak_count)的赋值构造函数(入参spa, shared类型,实际是浅拷贝了spa的计数器)后,给_M_weak_count加1;B.ba与spa的计数值, _M_use_count为1,_M_weak_count=2
- spa/spb(shared_ptr)析构时,如前分析,如果_M_use_count计数为0了就会释放数据成员,spa和spb的_M_use_count分别为1和2,调用各自的析构函数后首先spa计数减为0被释放
同理,如果没有使用weak_ptr,最终spa和spb的_M_use_count都为2,调用shared_ptr的析构函数,_Sp_counted_base两个计数值都无法减为0无法析构
make_shared
template<typename _Tp, typename _Alloc, typename... _Args>
inline shared_ptr<_Tp>
allocate_shared(const _Alloc& __a, _Args&&... __args)
{
return shared_ptr<_Tp>(_Sp_make_shared_tag(), __a,
std::forward<_Args>(__args)...); // 调用相关构造函数
}
// make_shared
template<typename _Tp, typename... _Args>
inline shared_ptr<_Tp>
make_shared(_Args&&... __args)
{
typedef typename std::remove_const<_Tp>::type _Tp_nc;
return std::allocate_shared<_Tp>(std::allocator<_Tp_nc>(),
std::forward<_Args>(__args)...);
}
shared_ptr为该构造函数提供了一个接口
private:
// This constructor is non-standard, it is used by allocate_shared.
// struct _Sp_make_shared_tag { };为了特化
template<typename _Alloc, typename... _Args>
shared_ptr(_Sp_make_shared_tag __tag, const _Alloc& __a,
_Args&&... __args)
: __shared_ptr<_Tp>(__tag, __a, std::forward<_Args>(__args)...)
{ }
__shared_ptr
template<typename _Alloc, typename... _Args>
__shared_ptr(_Sp_make_shared_tag __tag, const _Alloc& __a,
_Args&&... __args)
: _M_ptr(), _M_refcount(__tag, (_Tp*)0, __a,
std::forward<_Args>(__args)...)
{
// _M_ptr needs to point to the newly constructed object.
// This relies on _Sp_counted_ptr_inplace::_M_get_deleter.
void* __p = _M_refcount._M_get_deleter(typeid(__tag)); // 支持__GXX_RTTI时使用类型识别
_M_ptr = static_cast<_Tp*>(__p); // _M_ptr为新构建对象
// 设置enable_shared_from_this的_M_weak_this
__enable_shared_from_this_helper(_M_refcount, _M_ptr, _M_ptr);
}
enable_shared_from_this
enable_shared_from_this主要解决的问题:
派生类可以创建一个shared_ptr,并且让他指向已经存在的shared_ptr共享所有权
使用:
- 继承这个类,这样你就得到一个weak_ptr的成员,在构造shared_ptr时会为_M_weak_this赋值,使其指向你新构造的shared_ptr
- 使用shared_from_this获取这个派生类的_M_weak_this对应的shared_ptr指针
template<typename _Tp>
class enable_shared_from_this
{
protected:
constexpr enable_shared_from_this() noexcept { }
enable_shared_from_this(const enable_shared_from_this&) noexcept { }
enable_shared_from_this&
operator=(const enable_shared_from_this&) noexcept
{ return *this; } // 返回他自己
~enable_shared_from_this() { }
public:
shared_ptr<_Tp>
shared_from_this()
{ return shared_ptr<_Tp>(this->_M_weak_this); }
shared_ptr<const _Tp>
shared_from_this() const
{ return shared_ptr<const _Tp>(this->_M_weak_this); }
private:
template<typename _Tp1>
void
_M_weak_assign(_Tp1* __p, const __shared_count<>& __n) const noexcept
{ _M_weak_this._M_assign(__p, __n); } // 赋值weak_ptr的数据成员=__p,计数器=__n
// __enable_shared_from_this_helper功能:
// 使用输入参数:数据__px和shared_ptr __pe为weak_ptr赋值
template<typename _Tp1, typename _Tp2>
friend void
__enable_shared_from_this_helper(const __shared_count<>&,
const enable_shared_from_this<_Tp1>*,
const _Tp2*) noexcept;
mutable weak_ptr<_Tp> _M_weak_this; // 仅有成员变量,weak_ptr
};
使用weak_ptr构造__shared_ptr:
template<typename _Tp1>
explicit __shared_ptr(const __weak_ptr<_Tp1, _Lp>& __r)
: _M_refcount(__r._M_refcount) // may throw默认复制构造浅拷贝
{
__glibcxx_function_requires(_ConvertibleConcept<_Tp1*, _Tp*>)
// It is now safe to copy __r._M_ptr, as
// _M_refcount(__r._M_refcount) did not throw.
_M_ptr = __r._M_ptr;
}
待补充……