栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

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

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

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

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

class Widget
{
public:
    Widget();

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

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

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

在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;
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/628714.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号