实现私有化(Pimpl) --- QT常见的设计模式
转载自: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常见的设计模式的更多相关文章
- Android开发中常见的设计模式 MD
Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...
- Android开发中常见的设计模式
对于开发人员来说,设计模式有时候就是一道坎,但是设计模式又非常有用,过了这道坎,它可以让你水平提高一个档次.而在android开发中,必要的了解一些设计模式又是非常有必要的.对于想系统的学习设计模式的 ...
- iOS开发——高级篇——iOS中常见的设计模式(MVC/单例/委托/观察者)
关于设计模式这个问题,在网上也找过一些资料,下面是我自己总结的,分享给大家 如果你刚接触设计模式,我们有好消息告诉你!首先,多亏了Cocoa的构建方式,你已经使用了许多的设计模式以及被鼓励的最佳实践. ...
- iOS常见的设计模式
ios开发学习中,经常弄不清楚ios的开发模式,今天我们就来进行简单的总结和探讨~ (一)代理模式 应用场景:当一个类的某些功能需要由别的类来实现,但是又不确定具体会是哪个类实现.优势:解耦合敏捷原则 ...
- iOS 开发中常见的设计模式
最近有小伙伴问到在iOS开发中的几种设计模式,这里摘录一下别人的总结(因为已经感觉总结得差不多了,适用的可以阅读一下) 首先是开发中的23中设计模式分为三大类:1.创建型 2.结构型 3.行为型 (i ...
- Java几种常见的设计模式
--------------------- 本文来自 旭日Follow_24 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/xuri24/article/detail ...
- Android开发中常见的设计模式(二)——Builder模式
了解了单例模式,接下来介绍另一个常见的模式--Builder模式. 那么什么是Builder模式呢.通过搜索,会发现大部分网上的定义都是 将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建 ...
- 5.Qt model view设计模式
Introduction to Model/View Programming QT4 介绍了一系列新的 Item View 类,这些类使用Model/View结构来管理数据和数据如何呈现给 ...
- iOS中常见的设计模式(MVC/单例/委托/观察者)
关于设计模式这个问题,在网上也找过一些资料,下面是我自己总结的,分享给大家 如果你刚接触设计模式,我们有好消息告诉你!首先,多亏了Cocoa的构建方式,你已经使用了许多的设计模式以及被鼓励的最佳实践. ...
随机推荐
- vs2015智能提示英文改为中文
vs2015智能提示英文改为中文 C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework 进入 \v ...
- Hibernate的状态
最新的Hibernate文档中为Hibernate对象定义了四种状态(原来是三种状态,面试的时候基本上问的也是三种状态),分别是:瞬时态(new, or transient).持久态(managed, ...
- https://github.com/Lushenggang/show-pdf在线浏览pdf文件在线浏览pdf文件
在线浏览pdf文件 https://github.com/Lushenggang/show-pdf https://github.com/Lushenggang/show-pdf
- Linux下搭建redis服务器
1.redis需要gcc的编译环境,在线安装gcc:# yum install gcc-c++ 2.上传redis的压缩包到Linux系统,解压 3.进入源码目录,编译(注意要有makefile), ...
- js动态规划---背包问题
//每种物品仅有一件,可以选择放或不放 //即f[i][w]表示前i件物品恰放入一个容量为w的背包可以获得的最大价值. //则其状态转移方程便是:f[i][w]=max{f[i-1][w],f[i-1 ...
- Express web框架 upload file
哈哈,敢开源,还是要有两把刷子的啊 今天,看看node.js 的web框架 Express的实际应用 //demo1 upload file <html><head><t ...
- StackExchange.Redis在net中使用
redis 官网https://redis.io redis 下载 进入下载页面 https://redis.io/download https://github.com/MicrosoftArc ...
- sqli-labs(十一)(二次注入)
第二十四关: 这关考验的是sql的二次注入. 这关是一个登陆加注册的功能点,登陆的地方没有注入,账号密码都输入输入'",页面只会显示登陆失败. 但注册账号的地方没有做过滤可以注册带有单引符号 ...
- sourceInsight工具移除不掉项目 source Insight Add and Remove Project Files
问题描述: sourceInsight创建的项目,有时候会遇到Remove Project 报错,移除不成功的情况. 解决办法: 进入到sourceinsight的安装目录.删除掉保存的工程文件信息即 ...
- FZU 1683 纪念SlingShot(矩阵水)
纪念SlingShot [题目链接]纪念SlingShot [题目类型]矩阵水 &题解: 这代码调了十多分钟,结果是Mul没返回值,好zz啊. 令sum(n)=sum(n-1)+f(n) 那么 ...