1 参考

  1. 《effective C++》 条款31:将文件间的编译关系降至最低
  2. PIMPL Idiom: http://c2.com/cgi/wiki?PimplIdiom

2 什么是PIMPL?

  PIMPL是指pointer to implementation。通过使用指针的方式隐藏对象的实现细节。是实现“将文件间的编译依存关系降至最低”的方法之一。另一个方式是通过接口实现,但其原理一样。

  PIMPL又称作“编译防火墙”、“笑脸猫技术”,它只在C/C++等编译语言中起作用。

3 为什么要使用PIMPL?

  3.1 理论分析

  庞大的项目,修改一个文件之后,重新编译,所有依赖该文件的文件都需要重新编译,导致编译时间太长。

  3.2 工程实例

  通过描述一个实例来证明上一小节的理论。

    3.2.1 不使用PIMPL

  文件间的依赖关系如图:

  有三个源文件依赖“Person.h”,实际中可以有更多个文件依赖它,为了说明意思,我源码写的都非常简单,主要是为了表明文件间的依赖关系而已。

Person.h

#ifndef PERSON_H_
#define PERSON_H_
struct Person
{
void print();
};
#endif Person.cc #include "Person.h"
#include <iostream> void
Person::print()
{
std::cout << "Person::print()" << std::endl;
} PersonUser.cc #include "Person.h" main.cc
#include "Person.h"
int main()
{
return ;
} Makefile

#
# Makefile
# author: zhaokai
# date: --
# TESTS = main all : $(TESTS) clean :
rm -f $(TESTS)
rm -f main.o PersonUser.o Person.o main.o: main.cc Person.h
g++ -c main.cc PersonUser.o : PersonUser.cc Person.h
g++ -c PersonUser.cc
Person.o: Person.cc Person.h
g++ -c Person.cc $(TESTS): main.o PersonUser.o Person.o
g++ -o main main.o PersonUser.o Person.o

  现在我们开始修改Person.h文件:

#ifndef PERSON_H_
#define PERSON_H_ struct Person
{
int i; // add int i
void print();
};
#endif

  然后make,结果如下:

  依赖Person.h的三个文件都被重新编译了,最后链接生成执行文件。

    3.2.2 使用PIMPL

  使用PIMPL需要将Person类的实现移到PersonImpl类中,使用指针的方式将实现隐藏,相当于Person.h只是一个傀儡而已,而以前依赖它的文件依旧依赖之,文件间的依赖关系如图:

  有三个源文件依赖“Person.h”,实际中可以有更多个文件依赖它,有两个源文件和一个头文件依赖“PersonImpl.h”。

Person.h

#ifndef PERSON_H_
#define PERSON_H_
#include <memory> struct PersonImpl;
struct Person
{
void print();
private:
std::shared_ptr<PersonImpl> pImpl;
};
#endif Person.cc #include "Person.h"
#include "PersonImpl.h" void
Person::print()
{
pImpl->print();
} PersonImpl.h
#ifndef PERSONIMPL_H_
#define PERSONIMPL_H_ struct PersonImpl
{
void print();
};
#endif PersonImpl.cc #include "PersonImpl.h"
#include <iostream> void
PersonImpl::print()
{
std::cout << "PersonImpl::print()" << std::endl;
} PersonUser.cc
#include "Person.h" main.cc
#include "Person.h" int main()
{
return ;
} Makefile #
# Makefile
# author: zhaokai
# date: --
# TESTS = main all : $(TESTS) clean :
rm -f $(TESTS)
rm -f main.o PersonUser.o Person.o PersonImpl.o main.o: main.cc Person.h
g++ --std=c++ -c main.cc PersonUser.o : PersonUser.cc Person.h
g++ --std=c++ -c PersonUser.cc Person.o: Person.cc Person.h PersonImpl.h
g++ --std=c++ -c Person.cc PersonImpl.o: PersonImpl.cc PersonImpl.h
g++ --std=c++ -c PersonImpl.cc $(TESTS): main.o PersonUser.o Person.o PersonImpl.o
g++ -o main main.o PersonUser.o Person.o PersonImpl.o

  现在我们开始修改PersonImpl.h文件,注意这时候Person.h已经是傀儡了,如果想给Person增加属性那应该修改PersonImpl.h文件:

#ifndef PERSONIMPL_H_
#define PERSONIMPL_H_ struct PersonImpl
{
int i; // add int i
void print();
};
#endif

  然后make,结果如下:

  依赖PersonImpl.h的两个文件都重新编译了,而依赖于“Person.h”的文件main.cc和PersonUser.cc都没有重新编译。

    3.2.3 对比

  同样是一件事情,为Person类增加属性int i;两种方法导致编译的过程就不同,我们举得例子比较小,如果有100个类似PersonUser这样的文件,那么使用PIMPL,编译时还是只有“Person.cc”和“PersonImpl”两个文件重新编译了;但是不使用PIMPL的话,就是“main.cc”,“Person.cc”和100个类似“PersonUser.cc”这样的文件重新编译,那就是102个文件。

  通过上面的实例就可证明理论分析部分了。

  至于如何使用PIMPL,敬请期待下一篇文章!

PIMPL(一)的更多相关文章

  1. pImpl

    之前看代码,一直对pIml这个用法一知半解,参考这里 的一篇文章后有所收获. 总结一下,pIml的好处如下: 第一,引入更多的头文件降低编译速度.而且这个声明当然写在一个头文件里,而头文件,是不能预编 ...

  2. [021]转 C++ Pimpl机制

    出处:http://www.cnblogs.com/gnuhpc/ 1.简介 这个机制是Private Implementation的缩写,我们常常听到诸如“不要改动你的公有接口”这样的建议,所以我们 ...

  3. 何为 pimpl ?

    前言 你是否总因头文件包含冲突而苦恼? 你是否因头文件包含错乱而苦恼? 你是否因封装暴露了数据而苦恼? 你是否因经常改动实现而导致重新编译而苦恼? 在这里, 这些问题都不是问题, 跟随作者, 揭秘pi ...

  4. PIMPL设计模式的理解和使用

    以下两段不同程序的比较 //file a.h #include "a.h" #include “ b.h” class A{ void Fun(); B  b; } //file: ...

  5. 提高C++编译速度-------pimpl 模式& 桥接模式(转)

    pimpl 模式(Private Implementation),我们常常听到诸如“不要改动你的公有接口”这样的建议,所以我们一般都会修改私有接口,但是这会导致包含该头文件的所有源文件都要重新编译,这 ...

  6. C++ 编译期封装-Pimpl技术

    Pimpl技术——编译期封装 Pimpl 意思为“具体实现的指针”(Pointer to Implementation), 它通过一个私有的成员指针,将指针所指向的类的内部实现数据进行隐藏, 是隐藏实 ...

  7. Item 22: 当使用Pimpl机制时,在实现文件中给出特殊成员函数的实现

    本文翻译自<effective modern C++>,由于水平有限,故无法保证翻译完全正确,欢迎指出错误.谢谢! 博客已经迁移到这里啦 如果你曾经同过久的编译时间斗争过,那么你肯定对Pi ...

  8. 实现私有化(Pimpl) --- QT常见的设计模式

    转载自:http://blog.sina.com.cn/s/blog_667102dd0100wxbi.html 一.遇到的问题 1.隐藏实现 我们在给客户端提供接口的时候只希望能暴露它的接口,而隐藏 ...

  9. PIMPL(二)

    文档下载 上一篇文档,PIMPL(一) 1 如何使用PIMPL 有多种方式实现PIMPL,这里按照<Effective C++>中介绍的方式. 1.1 基本步骤 假设原有Person如下: ...

随机推荐

  1. 题目1100:最短路径(最短路径问题进阶dijkstra算法)

    题目链接:http://ac.jobdu.com/problem.php?pid=1100 详细链接:https://github.com/zpfbuaa/JobduInCPlusPlus 参考代码: ...

  2. powerDesigner根据sql脚本来逆向生成pdm等模型

    一.问题概述 网上一般的博文都是说要建立数据源的方式来逆向或者正向. 我这人比较懒得折腾,更喜欢通过sql脚本的方式来做. 二.步骤 File-->New Model--> 然后: 注意上 ...

  3. 记录一下使用Ubuntu16.0.4配置和使用docker registry

    h1, h2, h3, h4, h5, h6, p, blockquote { margin: 5px; padding: 5; } body { font-family: "Helveti ...

  4. wpgcms---碎片管理的使用

    这里很神奇的是碎片管理是编辑器,所以拿到的配置都是富文本,所以在前台作为字段来使用的时候,需要过滤掉字符串. 具体示例: {% set qq = wpg.fragment.get("qq&q ...

  5. pandas 数据预处理

    pandas 数据预处理 缺失数据处理 csv_data=''' A,B,C,D 1.0,2.0,3.0,4.0 5.6,6.0,,8.0 0.0,11.0,12.0,,''' import pand ...

  6. hdu3065 病毒侵袭持续中【AC自动机】

    病毒侵袭持续中 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Sub ...

  7. c++中inline函数的意义

    inline C++关键字,在函数声明或定义中函数返回类型前加上关键字inline,即可以把函数指定为内联函数.关键字inline必须与函数定义放在一起才能使函数成为内联,仅仅将inline放在函数声 ...

  8. 采用jquery同django实现ajax通信

    在网页访问中通过HTTP协议中的get/post文件发送数据或请求.在浏览器中输入url后,浏览器就会帮助我们完成请求的发送和返回,并刷新更新界面.但是,如果我们不想更新界面,仅仅是发送一个get/p ...

  9. HDU-2680 Choose the best route 单向边+反向dijkstra

    https://vjudge.net/problem/HDU-2680 题意:以起始点 终点 长度 给出一个图,已知可以从w个起点出发,求从任一起点到同一个终点s的最短路径.注意是单向边.m<1 ...

  10. Redis used_cpu_sys used_cpu_user meaning (redis info中cpu信息的含义)

    Redis 中 used_cpu_sys 和 used_cpu_user含义. 在Redis的info命令输出结果中有如下四个指标,redis官网给出了下面一段解释,但是还是不明白什么意思. used ...