Pimpl手法:把某类的数据成员用一个指涉到某实现类/结构提的指针代替,然后把原来的主类中的数据成员放到实现类中,并通过指针间接访问这些数据成员;
class Widget { public: Widget(); private: std::string name; std::vectordata; Gadget g1, g2, g3; // Gadget是某种用户自定义类型 };
如果头文件gadget.h的内容发生了改变,则Widget的客户必须重新编译。在C++98中改善手法如下:
class Widget { public: Widget(); ~Widget(); // 析构函数变得必须,理由见下 private: struct Impl; Impl *pImpl; };
由于Widget不再提及std::string,std::vector,gadget.h这些类型的头文件,会使得编译速度提升;
一个已经声明但未定义的类型成为非完整类型。Widget::Impl就是非完整类型;Pimpl用法第一部分:声明一个指针类型的数据成员,指涉到一个非完整类型;第二部分是动态分配和回收持有从前在原始类里面的那些数据成员的对象,而分配和回收代码放在实现文件中;
#include "widget.h" #include "gadget.h" #include#include struct Widget::Impl { std::string name; std::vector data; Gadget g1, g2, g3; // Gadget是某种用户自定义类型 }; Widget::Widget() : pImpl(new Impl){} Widget::~Widget(){delete Impl;}
上述实现把依赖从widget.h(对Widget客户可见并由他们使用)转移到了Widget.cpp(只对实现者可用并被实现)中;
使用智能的版本如下:
class Widget { public: Widget(); private: struct Impl; std::unique_ptrpImpl; // 使用只能指针代替 }
#include "widget.h" #include "gadget.h" #include#include struct Widget::Impl { std::string name; std::vector data; Gadget g1, g2, g3; // Gadget是某种用户自定义类型 }; Widget::Widget() : pImpl(std::make_unique ()){}
上述代码本身能通过编译,但是客户如下代码不能:
#include "widget.h" Widget w; // 报错,invalid application of ‘sizeof’ to incomplete type ‘Widget::Impl’ static_assert(sizeof(_Tp)>0,(在非完整类型实施了sizeof *** 作)
该问题产生的原因是w被析构时所生成的代码引起。在使用了std::unque_ptr的类定义里,我们未声明析构函数,编译器为我们自动生成一个。默认析构器是在std::unique_ptr内部使用delete运算符来针对裸指针实施析构函数。然而,在实施delete运算符之前,典型的实现会使用C++11中的static_assert去确保裸指针未指涉到非完整类型。
解决办法为:之需要保证在生产析构函数std::unque
class Widget { public: Widget(); ~Widget(); // 仅声明 private: struct Impl; std::unique_ptrpImpl; // 使用只能指针代替 }
在widget.cpp定义析构函数,位置在Widget::Impl之后
#include "widget.h" #include "gadget.h" #include#include struct Widget::Impl { std::string name; std::vector data; Gadget g1, g2, g3; // Gadget是某种用户自定义类型 }; Widget::Widget() : pImpl(std::make_unique ()){} Widget::~Widget() = default;
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)