IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 开发工具 -> 条款22:使用Pimpl习惯用法时,将特殊成员函数的定义放到实现文件中 -> 正文阅读

[开发工具]条款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;
  开发工具 最新文章
Postman接口测试之Mock快速入门
ASCII码空格替换查表_最全ASCII码对照表0-2
如何使用 ssh 建立 socks 代理
Typora配合PicGo阿里云图床配置
SoapUI、Jmeter、Postman三种接口测试工具的
github用相对路径显示图片_GitHub 中 readm
Windows编译g2o及其g2o viewer
解决jupyter notebook无法连接/ jupyter连接
Git恢复到之前版本
VScode常用快捷键
上一篇文章      下一篇文章      查看所有文章
加:2021-11-28 11:30:06  更:2021-11-28 11:31:45 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年12日历 -2024/12/24 3:38:30-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码
数据统计