实现私有化(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的构建方式,你已经使用了许多的设计模式以及被鼓励的最佳实践. ...
随机推荐
- (1.7)mysql profiles分析
mysql profiles分析 作用:记录会话查询SQL所用时间 1.开启 2.使用 [2.1]先使用一个查询 [2.2]然后再运行 show profiles; [2.3]查看执行过程中每个状态和 ...
- MongoDB pymongo模块 删除数据
使用user集合,删除user集合的数据 import pymongo mongo_client = pymongo.MongoClient( host='192.168.0.112', port=2 ...
- docker+Nexus Repository Manager 搭建私有docker仓库
使用容器安装Nexus3 1.下载nexus3的镜像: docker pull sonatype/nexus3 2.使用镜像启动一个容器: docker run -d -p 8081:8081 -p ...
- java poi导入Excel(个人代码)
案例使用的框架:jsp+spring+mybaties <form id="importForm" name="importForm" method=&q ...
- PHP数字字符串左侧补0、字符串填充和自动补齐的几种方法
一.数字补0. 如果要自动生成学号,自动生成某某编号,就像这样的形式“d0000009”.“d0000027”时,那么就会面临一个问题,怎么把左边用0补齐成这样8位数的编码呢?我想到了两种方法实现这个 ...
- python的__new__方法
https://www.cnblogs.com/kex1n/p/5991249.html https://blog.csdn.net/wwx890208/article/details/8053445 ...
- model browser 不出现时
1:当 创建 component 时, 创建完成后,没有出现model browser, 这时需要在model上面添加一个model,然后保存退出,重新进入,就会出现model browser
- Django进阶Model篇—数据库操作(ORM)
一.数据库配置 django 默认支持sqlite.mysql.oracle.postgresql数据库,像db2和sqlserver之类的数据库需要第三方的支持,具体详见https://docs.d ...
- Windows 下MySql Replication(复制)配置
环境准备 到官网下载mysql-installer-web-community-5.7.21.0.msi并安装,选择MySql Workbench,记录安装时root输入的密码. 需要安装在两台机器上 ...
- Java后台+数据库+Java web前端(新手)
实现简单页面上对数据的增删改查:Java后台+数据库表+Jsp前端网页设计 这里做一个简单的学生课程信息管理系统,做之前一定要先有自己的思路,要不然对新手来说,很容易乱的. 另有一完整的代码可供参考, ...