转载自: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. 1: //base.h
  1. 2: class Base
  1. 3: {
  1. 4: public:
  1. 5: void foo();
  1. 6: };
  1. 7:
  1. 8: //sub.h
  1. 9: #include "base.h"
  1. 10: class sub : public Base
  1. 11: {
  1. 12: public:
  1. 13: void go();
  1. 14: };

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

2.一个稍好点的方法

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

3.使用机制的情况

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

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

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

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

4.实践

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

//一个网上的例子

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

四、引来的其它的问题

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

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

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

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

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

直接可以用:

  1. 1: sample s;
  1. 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. 前端 HTML 标签分类

    三种: 1.块级标签: 独占一行,可设置宽度,高度.如果设置了宽度和高度,则就是当前的宽高.如果宽度和高度没有设置,宽度是父盒子的宽度,高度根据内容填充. 2.行内标签:在一行内显示,不能设置宽度,高 ...

  2. VS2017使用Git进行源代码管理

    步骤一:将解决方案添加到源代码管理 步骤二:进入团队资源管理器 双击存储库项目进入Git操作页面. 步骤三:同步本地代码到远程仓库 选择同步功能 步骤四:发布代码到Git 点击之后输入你要发布的git ...

  3. asp.net乱码问题

    1.html文件乱码 html文件是有编码方式的,比如"UTF-8"."GB2312". A.VS中在文件选项,文件另存为...,保存右边的下拉框编码保存... ...

  4. PHP数组对象对比机制

    $a = [1,2]; $b = [1,'2']; var_dump($a == $b); // true var_dump($a === $b); // false $c = ['ab'=>' ...

  5. PHP策略模式2

    <?php /** PHP 策略模式 * 策略模式是对象的行为模式,用意是对一组算法的封装.动态的选择需要的算法并使用. * 策略模式指的是程序中涉及决策控制的一种模式.策略模式功能非常强大,因 ...

  6. 利用 PorterDuff 动态改变资源色值,缩减安装包大小

    利用 PorterDuff 改变资源原有色值,从而实现只需要一个资源文件,就可以表示几种不同的状态,如在线或者离线等等 public Drawable colorDrawable(Resources ...

  7. 多态使用时,父类多态时需要使用子类特有对象。需要判断 就使用instanceof

    instanceof:通常在向下转型前用于健壮性的判断,判断是符合哪一个子类对象 package Polymorphic; public class TestPolymorphic { public ...

  8. Java通过POI生成Excel

    import java.io.FileOutputStream; import java.text.SimpleDateFormat; import java.util.ArrayList; impo ...

  9. python浮窗

    import wx; app = wx.App(); win = wx.Frame(None,title="老穆视频",pos=(900,20),size=(300,60),sty ...

  10. 基于python+Testlink+Jenkins实现的接口自动化测试框架V3.0

    基于python+Testlink+Jenkins实现的接口自动化测试框架V3.0 目录 1. 开发环境2. 主要功能逻辑介绍3. 框架功能简介 4. 数据库的创建 5. 框架模块详细介绍6. Tes ...