Pimpl技术——编译期封装

Pimpl 意思为“具体实现的指针”(Pointer to Implementation),

它通过一个私有的成员指针,将指针所指向的类的内部实现数据进行隐藏,

是隐藏实现,降低耦合性和分离接口实现的一个现代 C++ 技术,并有着“编译防火墙(compilation firewall)”的名头。

Pimpl技术的基本应用

其中利用了C++11的std::unique_ptr来让Impl指针的内存更易受控制。

此外由于声明了析构函数,导致默认的移动构造/赋值函数不能生成,若默认行为符合自己的需求,则需显式声明 = default

(当只在.h里,Impl是个不完整的类型,所以无法在.h类直接 = default,而是在.h声明,在.cpp使= default)

若需要给类提供拷贝性质的函数,需要额外花点心思处理std::unique_ptr(该智能指针不支持拷贝)。

// my_class.h
#pragma once
#include <memory> class my_class {
// ... 所有的公有/保护接口都可以放在这里 ...
my_class();
~my_class();
my_class(my_class&& v); //移动构造
my_class& operator=(my_class&& v); //移动赋值
private:
class Impl;
std::unique_ptr<Impl> pimpl;
};
// my_class.cpp
// ...include其它要依赖的头文件...
#include "my_class.h" class my_class::Impl {
// 在这里定义所有私有变量和方法(换句话说是my_class类的具体实现细节内容)
// 现在可以改变实现,而依赖my_class.h的其他类无需重新编译...
}; my_class::my_class():pimpl(std::make_unique<Impl>()){
// ...初始化pimpl...
}
my_class::~my_class() = default;
my_class::my_class(my_class&& v) = default;
my_class::my_class& operator=(my_class&& v) = default;

代码示例

//View.h文件
#pragma once
#include <memory> class View
{
public:
View();
~View();
View(View&& v);
View& operator=(View&& v);
void display();
private:
class Impl;
std::unique_ptr<Impl> pimpl;
};
//View.cpp文件
#include <iostream>
#include <string>
#include "View.h" /////////////////////////////////////////////////////////
//下面是View::Impl的定义,也就是体现了View类的具体实现细节 class View::Impl {
std::string name;
public:
Impl();
void printName();
}; View::Impl::Impl(){
name = "DefaultName";
} void View::Impl::printName(){
std::cout << "this is my name:" << name;
} ///////////////////////////////////////////////////////////
//下面是View类接口的实现 View::View():pimpl(std::make_unique<Impl>()){
} View::~View() = default;
View::View(View&& v) = default;
View& View::operator = (View&& v) = default; void View::display(){
pimpl->printName();
}

什么时候使用Pimpl技术?

可以看到Pimpl拥有如下优点:

  • 减少依赖项(降低耦合性):其一减少原类不必要的头文件的依赖,加速编译;其二对Impl类进行修改,无需重新编译原类。

  • 接口和实现的分离(隐藏了类的实现):私有成员完全可以隐藏在共有接口之外,给用户一个间接明了的使用接口,尤其适合闭源API设计。

  • 可使用惰性分配技术:类的某部分实现可以写成按需分配或者实际使用时再分配,从而节省资源。

Pimpl也拥有一些缺点:

  • 每个类需要占用小小额外的指针内存。

  • 每个类每次访问具体实现时都要多一个间接指针操作的开销,并且再使用、阅读和调试上都可能有所不便。

可以说,在性能/内存要求不敏感(非极端底层)的领域,Pimpl技术可以有相当不错的发挥和作用。

C++ 编译期封装-Pimpl技术的更多相关文章

  1. 通过宏封装实现std::format编译期检查参数数量是否一致

    背景 std::format在传参数量少于格式串所需参数数量时,会抛出异常.而在大部分的应用场景下,参数数量不一致提供编译报错更加合适,可以促进我们更早发现问题并进行改正. 最终效果 // 测试输出接 ...

  2. java编译期优化与执行期优化技术浅析

    java语言的"编译期"是一段不确定的过程.由于它可能指的是前端编译器把java文件转变成class字节码文件的过程,也可能指的是虚拟机后端执行期间编译器(JIT)把字节码转变成机 ...

  3. 【转】java注解处理器——在编译期修改语法树

    https://blog.csdn.net/a_zhenzhen/article/details/86065063 前言从需求说起由于相关政策,最近公司安全部要求各系统在rpc接口调用的交互过程中把相 ...

  4. Java编译期注解处理器详细使用方法

    目录 Java编译期注解处理器 启用注解处理器 遍历语法树 语法树中的源节点 语法树节点的操作 给类增加注解 给类增加import语句 构建一个内部类 使用方法 chainDots方法 总结 Java ...

  5. java编译期优化

    java语言的编译期其实是一段不确定的操作过程,因为它可以分为三类编译过程: 1.前端编译:把.java文件转变为.class文件 2.后端编译:把字节码转变为机器码 3.静态提前编译:直接把*.ja ...

  6. JVM-程序编译与代码早期(编译期)优化

    早期(编译期)优化 一.Javac编译器 1.Javac的源代码与调试 Javac的源代码放在JDK_SRC_HOME/langtools/src/shares/classes/com/sun/too ...

  7. Java注解(3)-注解处理器(编译期|RetentionPolicy.SOURCE)

    注解的处理除了可以在运行时通过反射机制处理外,还可以在编译期进行处理.在编译期处理注解时,会处理到不再产生新的源文件为止,之后再对所有源文件进行编译. Java5中提供了apt工具来进行编译期的注解处 ...

  8. JVM笔记——编译期的优化

    一.编译过程 解析和填充符号表的过程 插入注解处理器的注解处理过程 语义分析与字节码生成过程 二.解析和填充符号表 解析包含两个过程:词法分析和语法分析 (一)词法分析 将源代码的字符流转变成标记(T ...

  9. JVM总结(六):早期(编译期)优化

    这节我们来总结一下JVM编译器优化问题. JVM编译器优化 Javac编译器 Javac的源码和调试 解析与填充符号表 注解处理器 语法分析与字节码生成 Java语法糖 泛型和类型擦除 自动装箱.拆箱 ...

随机推荐

  1. 【树状数组】Bzoj1878[SDOI2009] HH的项链

    Description HH有一串由各种漂亮的贝壳组成的项链.HH相信不同的贝壳会带来好运,所以每次散步 完后,他都会随意取出一段贝壳,思考它们所表达的含义.HH不断地收集新的贝壳,因此, 他的项链变 ...

  2. BZOJ_1269&&1507_[AHOI2006]文本编辑器editor&&[NOI2003]Editor

    BZOJ_1269&&1507_[AHOI2006]文本编辑器editor&&[NOI2003]Editor 题意: 分析: splay模拟即可 注意1507的读入格式 ...

  3. BZOJ_3343_教主的魔法_分块+二分查找

    BZOJ_3343_教主的魔法_分块+二分查找 题意:教主最近学会了一种神奇的魔法,能够使人长高.于是他准备演示给XMYZ信息组每个英雄看.于是N个英雄们又一次聚集在了一起,这次他们排成了一列被编号为 ...

  4. i春秋------Misc更新

    今天早上起来很开森!因为今天要打比赛了(2018年3月安恒杯线上赛),等到比赛开始得时候,发现自己登陆不上去 想了很久发现自己只是预约了比赛,并没有报名(QAQ ),心疼一下傻傻的自己.现在开始工作: ...

  5. HrbustOJ 1564 螺旋矩阵

    Description 对于给定的一个数n,要你打印n*n的螺旋矩阵. 比如n=3时,输出: 1 2 3 8 9 4 7 6 5 Input 多组测试数据,每个测试数据包含一个整数n(1<=n& ...

  6. Dropwizard入门及开发步骤

    Dropwizard介绍 Dropwizard结构的服务组成 开发步骤 Dropwizard介绍 Dropwizard是一个微服务框架, 是各项技术的一个集成封装.它包含了以下组件: 嵌入式Jetty ...

  7. es6学习笔记-async函数

    1 前情摘要 前段时间时间进行项目开发,需求安排不是很合理,导致一直高强度的加班工作,这一个月不是常说的996,简直是936,还好熬过来了.在此期间不是刚学会了es6的promise,在项目有用到pr ...

  8. Fast R-CNN中的边框回归

    前面对R-CNN系的目标检测方法进行了个总结,其中对目标的定位使用了边框回归,当时对这部分内容不是很理解,这里单独学习下. R-CNN中最后的边框回归层,以候选区域(Region proposal)为 ...

  9. docker同时删除多个容器

    查询所有容器 sudo docker ps -a 同时删除多个符合筛选条件的容器,例如删除状态为“exited”的容器 docker rm $(docker container ls -f " ...

  10. BI基础知识