当前位置: 代码迷 >> 综合 >> Effective Modern C++ 笔记 第四章 Smart Pointers
  详细解决方案

Effective Modern C++ 笔记 第四章 Smart Pointers

热度:24   发布时间:2023-12-21 04:10:27.0
  1. Item 18:?Use std::unique_ptr for exclusive-ownership resource management.
  2. Item 19:?Use std::shared_ptr for shared-ownership resource management.
  3. Item 20:?Use std::weak_ptr for std::shared_ptr-like pointers that can dangle.
  4. Item 21:?Prefer std::make_unique and std::make_shared to direct use of new.
  5. 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包含如引用计数,弱引用计数等相关信息。

如果自定义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类型信息的一些影响。
  相关解决方案