实现私有化(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的构建方式,你已经使用了许多的设计模式以及被鼓励的最佳实践. ...
随机推荐
- git基础知识
Git的两大功能 1.协作开发 2.版本控制 版本库 版本库又名仓库,英文名repository,你可以简单理解成一个目录,这个目录里面的所有文件都可以被Git管理起来,每个文件的修改.删除,Git都 ...
- Nginx中的rewrite指令(break,last,redirect,permanent)
rewite 在server块下,会优先执行rewrite部分,然后才会去匹配location块 server中的rewrite break和last没什么区别,都会去匹配location,所以没必要 ...
- 从原型链看DOM--Node类型
前言: 本系列从原型,原型链,属性类型等方面下手学习了DOM文档对象模型,旨在弄清我们在DOM中常用的每一个属性和方法都清楚它从哪里来要到哪里做什么事,这样对于理解代码有一定启发.全靠自己在总结中摸索 ...
- [django]模板中自定义变量&django模板中的变量
django自定义模板变量 context_processors.py def mysetings(request): return { 'NAME': 'maotai' } settings.py ...
- [vue]组件篇
slot&子组件通过computed修改父组件数据 <div id="app"> <modal type="primary"> ...
- [wx]雪落香杉树人物关系图
雪落香杉树 开始的时候场面比较宏大,出场的人比较多.加上外国人名字没辨识度,所以容易乱 被告人: 宫本天道 9 10 11 12月被关77天(谋杀罪,9.16日早谋杀卡尔.海因)--妻子:初枝 白色衬 ...
- 查看npm仓库版本号
http://repo.inspur.com:8081/artifactory/webapp/#/artifacts/browse/simple/General/thirdparty iop 找到现在 ...
- [LeetCode] 709. To Lower Case_Easy
Implement function ToLowerCase() that has a string parameter str, and returns the same string in low ...
- Cocos Creator实现的《点我+1》
一.前言 在学习Cocos中,需要一些东西来练手,于是前段时间就开发仿照一款公司之前的产品<点我+1>来做,仿照过程中,所有的算法逻辑都是自己研究的,并没有参考公司代码,也没有使用公司的美 ...
- eclipse的调试模式以及断点运行
先在代码序号处打个点. 点击debug调试模式 然后再点击 如果有打断点,则会在断点处结束,如果没有,会一直一步一步向下直至结束.