-
Item 18: Use std::unique_ptr for exclusive-ownership resource management.
-
Item 19: Use std::shared_ptr for shared-ownership resource management.
-
Item 20: Use std::weak_ptr for std::shared_ptr-like pointers that can dangle.
-
Item 21: Prefer std::make_unique and std::make_shared to direct use of new.
-
Item 22: When using the Pimpl Idiom, define special member functions in the implementation file.
这一章主要讲unique_ptr, shared_ptr, weak_ptr, 读起来相对轻松,毕竟这些概念早已经广泛应用了。要点如下:
unique_ptr在默认的deleter情况下可以达到裸指针的大小。如果指定deleter会增加空间。shared_ptr相对来说overhead就会大些,shared_ptr除了维护指向object的指针,当然还需要维护一个“control block”。control block包含如引用计数,弱引用计数等相关信息。
unique_ptr在默认的deleter情况下可以达到裸指针的大小。如果指定deleter会增加空间。shared_ptr相对来说overhead就会大些,shared_ptr除了维护指向object的指针,当然还需要维护一个“control block”。control block包含如引用计数,弱引用计数等相关信息。
如果自定义deleter,那么这个deleter的类型对于unique_ptr来说是必须的,它是模板参数之一也就是unique_ptr的类型信息的一部分。而shared_ptr则不需要,它只需要目标对象的类型。所以shared_ptr显然更灵活。如果两个unique_ptr是不同的deleter类型的话,它们的类型是不同的;而同类型的shared_ptr则完全可以有不同的deleter。
那么这么做的原因呢?没想太明白。查了下一个还可以的解释是:
stackoverflow。也就是unique_ptr是以raw pointer为目标而不引入任何overhead的。引入deleter的类型信息就可以在编译期将unique_ptr的操作完全交给编译器进行进一步优化。
由于smart pointer的存在,消灭裸指针不仅是一个可选项而是必选项。如果同时使用smart pointer和裸指针就意味着小心踩坑(比如shared_ptr,你有一个raw pointer但是搞出两个control block在不同的shared_ptr里面。。。)。而对于shared_ptr来说,make_shared会是一种更高效的使用方式,原因在于前面提到了shared_ptr要维护control block,如果使用make_shared来同时完成object和control block的内存分配,就给了库代码很多的优化余地,比如把control block和object分配在一块内存。。。
另外c++里面很著名的一个指针就是this了。如果要对this使用shared_ptr,那么就需要继承自enable_shared_from_this并使用shared_from_this了。enable_shared_from_this代码如下:
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); }
template<typename _Tp1>
friend void
__enable_shared_from_this_helper(const __shared_count<>& __pn,
const enable_shared_from_this* __pe,
const _Tp1* __px) noexcept
{
if (__pe != 0)
__pe->_M_weak_assign(const_cast<_Tp1*>(__px), __pn);
}
mutable weak_ptr<_Tp> _M_weak_this;
};
假设类Test继承自enable_shared_from_this。enable_shared_from_this维护了一个weak_ptr,需要的时候就转一个shared_ptr出去。这个weak_ptr的对象指针,control block等都是通过__enable_shared_from_this_helper手动指定的。当对Test进行shared_ptr的构建操作时,会调用Test对象的__enable_shared_from_this_helper,将weak_ptr设置为正确的值,自然而然也就可以通过shared_from_this获得一个新的shared_ptr。
可以看出,调用shared_from_this之前,一定要已经存在Test的shared_ptr(control block已经存在了)。毕竟enable_shared_from_this和shared_from_this只是返回正确的shared_ptr,而不是创建正确的shared_ptr。
另外一个非常可疑的地方就是__enable_shared_from_this_helper的参数:const enable_shared_from_this* __pe,竟然没有模板参数。。。直接扔到stackoverflow,看解释:
stackoverflow
再说weak_ptr,除了它带来的好处,一个问题是weak_ptr使得control block和object的生命周期变得不一样了,也就是存在weak_ptr但是不存在对应的shared_ptr时,control block的内存是无法回收的。control block和object分别回收似乎也没啥问题,但是如果两者的内存由于优化是放在一起的话。。。item21对这个问题进行了详细的介绍。
item22 pimpl的例子也很有趣。部分可以说明unique_ptr类型中包含deleter类型信息的一些影响。