读书笔记 《Effective modern C++》之 Smart pointer(四)

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.


make函数的缺点

  • 不能传入自定义deleter
  • make函数使用括号初始化,比如std::make_unique<std::vector>(10, 20)创建了一个有着20个值为10的元素的vector,而不是创建了{10, 20}这么两个元素的vector。
  • 管理的对象和control block分配在一块内存上,导致被管理对象和control block占用的内存要同时回收。即,如果还有std::weak_ptr存在,control block就要在,对象占用的内存也没办法回收。比如shared_count为0,只存在weak_count,虽然expired了还是不能释放对象的内存。

Item 22: When using the Pimpl Idiom, define special member functions in the implementation file.

Pimpl 是一种接口和实现分离的方法,减少类的dependency来加快编译的方法。

简单的说就是

  • 把类所有的私有成员放到新声明的类中 - like PrivateImpl class
  • 在类的文件中,只做向前声明 PrivateImpl * // 向前声明,需要是指针
  • 在相应的cpp文件中实现PrivateImpl
  • 这样改变了PrivateImpl的实现,类也不需要重新编译(类中的PrivateImpl只是一个向前声明,接口并没有改变)

常见的非pimpl实现

// Fridge.h
#include "Engine.h"
 
class Fridge
{
public:
   void coolDown();
private:
   Engine engine_;
};

// Fridge.cpp
#include "Fridge.h"
 
void Fridge::coolDown()
{
   /* ... */
}

pimpl实现接口和实现分离后不需要再Fridge.h中引入Engine.h(所以Engine.h修改不影响Fridge类,而Fridge类只负责forward the call和管理impl_的资源)。
pointers only need a forward declaration to compile。所以Fridge不需要知道FridgeImpl所有的definition

// Fridge.h 
// 接口实现分离
class Fridge
{
public:
   Fridge();
   ~Fridge();
 
   void coolDown();
private:
   class FridgeImpl;
   FridgeImpl* impl_;
};
// Fridge.cpp
#include "Engine.h"
#include "Fridge.h"
 
class Fridge::FridgeImpl
{
public:
   void coolDown()
   {
      /* ... */
   }
private:
   Engine engine_;
};
 
Fridge::Fridge() : impl_(new FridgeImpl) {}
 
Fridge::~Fridge()
{
   delete impl_;
}
 
void Fridge::coolDown()
{
   impl_->coolDown();
}

如果转成unique_ptr有个坑需要注意。下面的实现是无法编译的,因为如果指向的类型不完整时(只有向前声明),unique_ptr会拒绝编译。

deleting a pointer leads to undefined behaviour if:

  • this pointer has type void*, or
  • the type pointed to is incomplete, that is to say is only forward declared, like FridgeImpl in our header file.

std::unique_ptr happens to check in its destructor if the definition of the type is visible before calling delete. So it refuses to compile and to call delete if the type is only forward declared.

Since we removed the declaration of the destructor in the Fridge class, the compiler took over and defined it for us. But compiler-generated methods are declared inline, so they are implemented in the header file directly. And there, the type of FridgeImpl is incomplete. Hence the error.

就是一个delete不完整类指针的问题
https://blog.csdn.net/weixin_33709364/article/details/90326569

// Fridge.h
#include <memory>
 
class Fridge
{
public:
   Fridge();
   void coolDown();
private:
   class FridgeImpl;
   std::unique_ptr<FridgeImpl> impl_;
};

// Fridge.cpp
#include "Engine.h"
#include "Fridge.h"
 
class FridgeImpl
{
public:
   void coolDown()
   {
      /* ... */
   }
private:
   Engine engine_;
};
 
Fridge::Fridge() : impl_(new FridgeImpl) {}

修改方法就是显示声明Fridge的析构函数,并且把实现放到FridgeImpl实现之后

#include <memory>
 
class Fridge
{
public:
   Fridge();
   ~Fridge();
   void coolDown();
private:
   class FridgeImpl;
   std::unique_ptr<FridgeImpl> impl_;
};

#include "Engine.h"
#include "Fridge.h"
 
class FridgeImpl
{
public:
   void coolDown()
   {
      /* ... */
   }
private:
   Engine engine_;
};
 
Fridge::Fridge() : impl_(new FridgeImpl) {}
 
Fridge::~Fridge() = default;

记得如果显示声明了析构函数,需要拷贝构造和移动构造时候就要自己补上。要注意的是,使用shared_ptr不需要这样。

class Widget { // in "widget.h"
public:
Widget();
      … // no declarations for dtor or move operations
private:
      struct Impl;
      std::shared_ptr<Impl> pImpl; // std::shared_ptr
}; // instead of std::unique_ptr

Widget w1;
auto w2(std::move(w1)); // move-construct w2
w1 = std::move(w2); // move-assign w1

这是因为shared_ptr和unique_ptr对自定义deleter的支持实现是不一样的。unique_ptr的deleter是模板参数(EBO优化),而shared_ptr的deleter是构造函数的一部分(成员对象)。

https://fuzhe1989.github.io/2017/05/19/cpp-different-role-of-deleter-in-unique-ptr-and-shared-ptr/
https://www.bfilipek.com/2018/01/pimpl.html
https://light-city.club/sc/src_analysis/stl/谈谈STL设计之EBO优化/

posted @ 2020-08-01 04:58  linsinan1995  阅读(142)  评论(0)    收藏  举报