shareptr

为什么使用std::make_shared
std::shared_ptr是常用的智能指针,建立一个shared_ptr对象有两种方式:
// (1)
std::shared_ptr<Widget> p1(new Widget);
// (2)
std::shared_ptr<Widget> p2(std::make_shared<Widget>());
通常方法(2)使用make_shared是更受推荐的做法。原因是
减少重复代码
对于
// (2)
std::shared_ptr<Widget> p2(std::make_shared<Widget>());
它可以简化为
// (2)
auto p2(std::make_shared<Widget>());
对比(1)少书写了一次Widget,在代码中减少重复总是一件好事,对吧:)
效率更高
对于
// (1)
std::shared_ptr<Widget> p1(new Widget);
存在两次内存分配操作:
1.new Widget
2.为p1分配控制块(control block),控制块用于存放引用计数等信息
而对于
// (2)
auto p2(std::make_shared<Widget>());
只有一次内存内配操作,make_shared会一次性申请足够大的空间用于存放Widget对象和智能指针的控制块。
在MSVC版本的STL中,make_shared的实现是
template<class _Ty,
class... _Types> inline
shared_ptr<_Ty> make_shared(_Types&&... _Args)
{ // make a shared_ptr
_Ref_count_obj<_Ty> *_Rx =
new _Ref_count_obj<_Ty>(_STD forward<_Types>(_Args)...);
shared_ptr<_Ty> _Ret;
_Ret._Resetp0(_Rx->_Getptr(), _Rx);
return (_Ret);
}
这里的_Ref_count_obj类包含成员变量:
1.控制块
2.一个内存块,用于存放智能指针管理的资源对象
所以new _Ref_count_obj能一次性为控制块和资源对象申请内存。
感兴趣的读者可以再看看_Ref_count_obj的构造函数:
template<class... _Types>
_Ref_count_obj(_Types&&... _Args)
: _Ref_count_base()
{ // construct from argument list
::new ((void *)&_Storage) _Ty(_STD forward<_Types>(_Args)...);
}
此处其实也有一个new操作,但是是placement new,不涉及内存分配。所以内存分配操作还是只有一次。
引用计数在Ref_count_obj的父类_Ref_count_base中。而_Storage就是存放资源对象的内存块。
placement new 会在已经开辟的空间中存放这个sotrage对象,即把对象放在指定的空间位置
那_Storage是怎么来的?它其实是一个联合体,编译器在编译时能获取到资源对象的大小,然后利用模板实例化出具有相等大小的联合体或结构体,这个联合体或结构体的对象就可以用于存放资源对象。

unique_ptr

此处,unique_ptr采用了移动拷贝构造,虽然其普通拷贝构造是允许的,但是右值拷贝构造是允许的,因为编译器知道当前拷贝的这个对象将会被销毁,因此其仍然会调用该拷贝函数
左值和右值

如 函数返回的值为右值,nullptr为字面量为右值
函数返回引用时返回的为左值
weak_ptr
_Atomic_word _M_use_count; // #shared
_Atomic_word _M_weak_count; // #weak + (#shared != 0)
由上述代码可见,实际上share_ptr和weak_ptr上都有指向一个计数类,包括对share_ptr的计数和对weak_ptr+(#shared!=0)的计数
即对于weak的计数来说,就算没有任何weak_ptr,其初始化值仍为1
那么weak_ptr什么时候判断share==0了呢?
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)//此处 当share_ptr被释放时,会检测有无weak_ptr对象仍然引用了该计数,如果没有了,才会释放该计数器
{
_GLIBCXX_SYNCHRONIZATION_HAPPENS_AFTER(&_M_weak_count);
_M_destroy();
}
}
}
