条款22:使用Pimpl习惯用法时,将特殊成员函数的定义放到实现文件中

Pimpl手法:把某类的数据成员用一个指涉到某实现类/结构提的指针代替,然后把原来的主类中的数据成员放到实现类中,并通过指针间接访问这些数据成员;

class Widget
{
public:
    Widget();

private:
    std::string name;
    std::vector<double> data;
	Gadget g1, g2, g3;	// Gadget是某种用户自定义类型
};

如果头文件gadget.h的内容发生了改变,则Widget的客户必须重新编译。在C++98中改善手法如下:

class Widget
{
public:
    Widget();
    ~Widget();	// 析构函数变得必须,理由见下

private:
	struct Impl;
	Impl *pImpl;
};

由于Widget不再提及std::string,std::vectorgadget.h这些类型的头文件,会使得编译速度提升;

一个已经声明但未定义的类型成为非完整类型。Widget::Impl就是非完整类型;Pimpl用法第一部分:声明一个指针类型的数据成员,指涉到一个非完整类型;第二部分是动态分配和回收持有从前在原始类里面的那些数据成员的对象,而分配和回收代码放在实现文件中;

#include "widget.h"
#include "gadget.h"
#include <string>
#include <vector>

struct Widget::Impl {
	std::string name;
    std::vector<double> 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_ptr<Impl> pImpl;	// 使用只能指针代替
}
#include "widget.h"
#include "gadget.h"
#include <string>
#include <vector>

struct Widget::Impl {
	std::string name;
    std::vector<double> data;
	Gadget g1, g2, g3;	// Gadget是某种用户自定义类型
};

Widget::Widget() : pImpl(std::make_unique<Impl>()){}

上述代码本身能通过编译,但是客户如下代码不能:

#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<Widget::Impl>代码处,Widget::Impl是个完整类型即可。只要类型的定义可以被看到,它就是完整的。因此。成功编译的关键在于让编译器看到Wideget的析构函数的函数体(编译器将要生成代码来析构std::unique_ptr类型数据成员之处)的位置在widget.cpp内部的Widget::Impl之后

class Widget
{
public:
    Widget();
    ~Widget(); // 仅声明

private:
	struct Impl;
	std::unique_ptr<Impl> pImpl;	// 使用只能指针代替
}

widget.cpp定义析构函数,位置在Widget::Impl之后

#include "widget.h"
#include "gadget.h"
#include <string>
#include <vector>

struct Widget::Impl {
	std::string name;
    std::vector<double> data;
	Gadget g1, g2, g3;	// Gadget是某种用户自定义类型
};

Widget::Widget() : pImpl(std::make_unique<Impl>()){}
Widget::~Widget() = default;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值