PIMPL 模式

六月 03, 2022 [c++] #c++

PIMPL 模式

PIMPL:很实用的一种基础模式。

PIMPL解释

PIMPL(Private Implementation 或 Pointer to Implementation)是通过一个私有的成员指针,将指针所指向的类的内部实现数据进行隐藏。

PIMPL的优点:

1)降低模块的耦合。因为隐藏了类的实现,被隐藏的类相当于原类不可见,对隐藏的类进行修改,不需要重新编译原类。 2)降低编译依赖,提高编译速度。指针的大小为(32位)或8(64位),X发生变化,指针大小却不会改变,文件c.h也不需要重编译。 3)接口与实现分离,提高接口的稳定性。

Pimpl idiom using shared_ptr working with incomplete types

shared_ptr处理不完整类型的Pimpl用法

使用pimpl指向实现类unique_ptr,需要完整类型的特殊成员函数(例如析构函数)的问题. 这是因为unique_ptr在delete p之前,默认删除器静态断言要删除的类型是否完整. 所以任何类特殊的成员函数必须在实现文件中定义.

《Effective Modern C++》的Item22有详细讲解Pimpl, 其中提到如果使用的是shared_ptr智能指针,则不需要在实现文件中定义特殊的成员函数shared_ptr,这源于它支持自定义删除器的方式

pImpl指针的std::unique_ptr和std::shared_ptr之间的行为差异源于智能指针是否支持自定义删除器的不同. 对于std::unique_ptr,删除器的类型是智能指针类型的一部分,这使编译器可以生成更小的运行时数据结构和更快的运行时代码.这种更高效率的结果是,当使用编译器生成的特殊函数(例如,析构函数或移动操作)时,指向类型必须是完整的. 对于std::shared_ptr,删除器的类型不是智能指针类型的一部分.这需要更大的运行时数据结构和稍慢的代码,但是当使用编译器生成的特殊函数时,指向的类型不需要完整.

但是为什么shared_ptr没有完整类型的Pimpl仍然可以工作. 没有编译器错误的唯一原因似乎是shared_ptr没有像unique_ptr那样的静态断言,但是由于缺少断言是否可能会发生未定义的运行时行为????

the deleter's created position for shared_ptr

The deleter for a shared pointer is created here:

Widget::Widget(): pImpl(new Impl) {}

until that point, all the shared pointer has is the equivalent of a std::funciton<void(Impl*)>. When you construct a shared_ptr with a T*, it writes a deleter and stores it in the std::function equivalent. At that point the type must be complete. So the only functions you have to define after Impl is fully defined are those that create a pImpl from a T* of some kind.

请记住:

Pimpl惯用法通过减少在类实现和类使用者之间的编译依赖来减少编译时间。 对于std::unique_ptr类型的pImpl指针,需要在头文件的类里声明特殊的成员函数,但是在实现文件里面来实现他们。即使是编译器自动生成的代码可以工作,也要这么做。 以上的建议只适用于std::unique_ptr,不适用于std::shared_ptr。

链接

https://github.com/CnTransGroup/EffectiveModernCppChinese/blob/master/4.SmartPointers/item22.md https://www.zhihu.com/question/310052411/answer/2227432856 https://stackoverflow.com/questions/40619984/pimpl-idiom-using-shared-ptr-working-with-incomplete-types