转载自:http://blog.sina.com.cn/s/blog_667102dd0100wxbi.html

一、遇到的问题

1.隐藏实现

我们在给客户端提供接口的时候只希望能暴露它的接口,而隐藏它的实现或者算法。这时候,至少至少有两种选择:

(1)写一个抽象类, 然后继承它

(2)使用PIMPL, 把实现放到内部公有的文件里,而对外部隐藏起来

2.重新编译

当我们有一个很大的工程的时候,我们一个底层的头文件不希望被修改,因为这会导致包含该头文件的所有源文件都要重新编译。

二、什么是PIMPLl机制

1.Private Implementation

直接的字面意思就是“实现私有化”,也如我们常常听到诸如“不要改动你的公有接口”这样的建议,Pimpl机制,顾名思义,将实现私有化,力图使得头文件对改变不透明。主要作用是解开类的使用接口和实现的耦合。

2.pointer to implementation

这种说法语义上更关注代码的实现方法,也就是一个指向实现的指针。

3.桥接模式

其实,这也是一个简单的桥接模式

三、具体分析

1.不使用PIMPL的情况

1: //base.h
2: class Base
3: {
4: public:
5: void foo();
6: };
7:
8: //sub.h
9: #include "base.h"
10: class sub : public Base
11: {
12: public:
13: void go();
14: };

可以看到,如果有base中加了一个新的成员函数或者只要做过改动,那么它的子类sub这个文件都是重新编译才行。在一个大工程中,这样的修改可能导致重新编译时间的激增。

2.一个稍好点的方法

一般来说,不在头文件中包含头文件是一个比较好的习惯,但是这也不能完全消除修改base.h带来的重新编译代价。一个稍好点的方法就是只在sub.cpp中包含base.h,但这还是要重新编译的,只是在表现上更完美了一些。

3.使用机制的情况

我们使用前置声明一个Impl类,并将这个类的一个指针实例放入主类中。之后我们只修改Impl类内部私有的内容。

1: //base.h
2: class Imp;
3: class Base
4: {
5: public:
6: void foo();
7: private:
8: Imp* pImp;
9: };

除非我们修改base的公有接口,否则这个头文件是不会被修改了。然后,我们用这个Impl类的实现来完成主类的细节实现,在主类的构造函数中,我们完成了实现类指针的实例化:

1: //cpp中包含实现类的头文件
2: #include "imp.h"
3:
4: Base::Base()
5: :pImp(new Imp)
6: {
7: }
8:
9: //调用实现类
10: Base::foo()
11: {
12: pImp->foo();
13: }
14: Base::~Base()
15: {
16: try
17: {
18: delete pImp;
19: }
20: catch (...)
21: {
22: }
23: }
24:
25: //这是真正的实现
26: Imp::foo()
27: {
28: //do...xxx
29: }

4.实践

在实践中,常常采用内部类来完成Pimpl机制

//一个网上的例子

1: // header
2: class fruit
3: {
4: public:
5: private:
6: class impl;
7: impl* pimpl_;
8: }
9:
10: // implementation
11: class fruit::impl
12: {
13:
14: };
15:
16: fruit::fruit()
17: {
18: pimpl_ = new impl();
19: }

四、引来的其它的问题

1.效率问题,每一个类的增加,肯定会增加开销。

2.这种机制不一定就是最好的机制,最简单的机制才是最好的机制

3.在构造和析构的时候,由于我们要new并且要保证delete,会引出RAII原则中的资源管理类的拷贝行为问题

所以,更好的办法是我们在使用这个机制的时候可以使用一个比较安全的智能指针,比如scoped_ptr和shared_ptr,但shared_ptr通常更合适,因为它支持拷贝和赋值。

1: class sample
2: {
3: private:
4: class impl; //不完整的内部类声明
5: shared_ptr<impl> p; //shared_ptr成员变量
6: public:
7: sample(); //构造函数
8: void print(); //提供给外界的接口
9: };
10:
11: //在sample的cpp中完整定义impl类和其他功能:
12: class sample::impl //内部类的实现
13: {
14: public:
15: void print()
16: {
17: cout << "impl print" << endl;
18: }
19: };
20:
21: //构造函数初始化shared_ptr void sample::print()
22: sample::sample():p(new impl){}
23: {
24: //调用pimpl实现print()
25: p->print();
26: }

直接可以用:

1: sample s;
2: s.print();

(在more effective C++ Item M14中有这个问题的讨论,之后我们再做学习讨论)

实现私有化(Pimpl) --- QT常见的设计模式的更多相关文章

  1. Android开发中常见的设计模式 MD

    Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...

  2. Android开发中常见的设计模式

    对于开发人员来说,设计模式有时候就是一道坎,但是设计模式又非常有用,过了这道坎,它可以让你水平提高一个档次.而在android开发中,必要的了解一些设计模式又是非常有必要的.对于想系统的学习设计模式的 ...

  3. iOS开发——高级篇——iOS中常见的设计模式(MVC/单例/委托/观察者)

    关于设计模式这个问题,在网上也找过一些资料,下面是我自己总结的,分享给大家 如果你刚接触设计模式,我们有好消息告诉你!首先,多亏了Cocoa的构建方式,你已经使用了许多的设计模式以及被鼓励的最佳实践. ...

  4. iOS常见的设计模式

    ios开发学习中,经常弄不清楚ios的开发模式,今天我们就来进行简单的总结和探讨~ (一)代理模式 应用场景:当一个类的某些功能需要由别的类来实现,但是又不确定具体会是哪个类实现.优势:解耦合敏捷原则 ...

  5. iOS 开发中常见的设计模式

    最近有小伙伴问到在iOS开发中的几种设计模式,这里摘录一下别人的总结(因为已经感觉总结得差不多了,适用的可以阅读一下) 首先是开发中的23中设计模式分为三大类:1.创建型 2.结构型 3.行为型 (i ...

  6. Java几种常见的设计模式

    --------------------- 本文来自 旭日Follow_24 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/xuri24/article/detail ...

  7. Android开发中常见的设计模式(二)——Builder模式

    了解了单例模式,接下来介绍另一个常见的模式--Builder模式. 那么什么是Builder模式呢.通过搜索,会发现大部分网上的定义都是 将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建 ...

  8. 5.Qt model view设计模式

    Introduction to Model/View Programming       QT4 介绍了一系列新的 Item View 类,这些类使用Model/View结构来管理数据和数据如何呈现给 ...

  9. iOS中常见的设计模式(MVC/单例/委托/观察者)

    关于设计模式这个问题,在网上也找过一些资料,下面是我自己总结的,分享给大家 如果你刚接触设计模式,我们有好消息告诉你!首先,多亏了Cocoa的构建方式,你已经使用了许多的设计模式以及被鼓励的最佳实践. ...

随机推荐

  1. OpenWrt 路由系统上抓包

    版权声明:本文为博主原创文章.未经博主同意不得转载. https://blog.csdn.net/qianguozheng/article/details/32108093 前言: 做路由器开发,难免 ...

  2. 用PowerDesigner建立概念模型的问题:不能创建相同字段名的关键字段

    依次点击Tools--->Model Options->Model Settings,在Model Settings中有Data Item组框,取消里面的Unique Code,勾选All ...

  3. 282A

    #include <iostream> #include <string> using namespace std; int main() { int n, plus, sub ...

  4. Xgboost调参总结

    一.参数速查 参数分为三类: 通用参数:宏观函数控制. Booster参数:控制每一步的booster(tree/regression). 学习目标参数:控制训练目标的表现. 二.回归 from xg ...

  5. apache分割数组和集合的分法

    public static void main(String[] agrs){    String[] array={"1","2","", ...

  6. System.Web.HttpException (0x80004005): 验证视图状态 MAC 失败。如果此应用程序由网络场或群集承载,请确保 <machineKey> 配置指定了相同的 validationKey 和验证算法。不能在群集中使用 AutoGenerate。

    异常描述: Global.asax捕获到异常:System.Web.HttpException (0x80004005): 验证视图状态 MAC 失败.如果此应用程序由网络场或群集承载,请确保 < ...

  7. 地图服务报 error #2035

    参考:https://blog.csdn.net/iteye_20296/article/details/82395628 现在问题解决了,确实是config.xml里关于这个widget的配置url ...

  8. MyBatis基础入门《十五》ResultMap子元素(collection)

    MyBatis基础入门<十五>ResultMap子元素(collection) 描述: 见<MyBatis基础入门<十四>ResultMap子元素(association ...

  9. 462. 最少移动次数使数组元素相等 II

    给定一个非空整数数组,找到使所有数组元素相等所需的最小移动数,其中每次移动可将选定的一个元素加1或减1. 您可以假设数组的长度最多为10000. 例如: 输入: [1,2,3] 输出: 2 说明: 只 ...

  10. 使用spring的特殊bean完成分散配置

    1.在使用分散配置时,spring的配置文件applicationContext.xml中写法如下: <!-- 引入db.properties文件, --> <context:pro ...