PIMPL 模式的实现及应用
2013-04-21 20:59
260 查看
pImpl惯用手法的运用方式大家都很清楚,其主要作用是解开类的使用接口和实现的耦合。如果不使用pImpl惯用手法,代码会像这样:
//c.cc
#include<x.h>
class C
{
public:
void f1();
private:
X x; //与X的强耦合
};
像上面这样的代码,类C与它的实现就是强耦合的,从语义上说,x成员数据是属于C的实现部分,不应该暴露给用户。从语言的本质上来说,在用户的代码中,每一次使用”new
C”和”C c1”这样的语句,都会将X的大小硬编码到编译后的二进制代码段中(如果X有虚函数,则还不止这些)——这是因为,对于”new
C”这样的语句,其实相当于operator new(sizeof(C) )后面再跟上C的构造函数,而”C
c1”则是在当前栈上腾出sizeof(C)大小的空间,然后调用C的构造函数。因此,每次X类作了改动,使用c.cc的源文件都必须重新编译一次,因为X的大小可能改变了。
在一个大型的项目中,这种耦合可能会对build时间产生相当大的影响。
pImpl惯用手法可以将这种耦合消除,使用pImpl惯用手法的代码像这样:
//c.cc
class X; //用前导声明取代include
class C
{
...
private:
X* pImpl; //声明一个X*的时候,class X不用完全定义
};
在一个既定平台上,任何指针的大小都是相同的。之所以分为X*,Y*这些各种各样的指针,主要是提供一个高层的抽象语义,即该指针到底指向的是那个类的对象,并且,也给编译器一个指示,从而能够正确的对用户进行的操作(如调用X的成员函数)决议并检查。但是,如果从运行期的角度来说,每种指针都只不过是个32位的长整型(如果在64位机器上则是64位,根据当前硬件而定)。
正由于pImpl是个指针,所以这里X的二进制信息(sizeof(C)等)不会被耦合到C的使用接口上去,也就是说,当用户”new
C”或”C c1”的时候,编译器生成的代码中不会掺杂X的任何信息,并且当用户使用C的时候,使用的是C的接口,也与X无关,从而X被这个指针彻底的与用户隔绝开来。只有C知道并能够操作pImpl成员指向的X对象。
测试用例:
//cls.h
//c.cc
//pmipl.h
//pmiph.cc
//main.cc
//c.cc
#include<x.h>
class C
{
public:
void f1();
private:
X x; //与X的强耦合
};
像上面这样的代码,类C与它的实现就是强耦合的,从语义上说,x成员数据是属于C的实现部分,不应该暴露给用户。从语言的本质上来说,在用户的代码中,每一次使用”new
C”和”C c1”这样的语句,都会将X的大小硬编码到编译后的二进制代码段中(如果X有虚函数,则还不止这些)——这是因为,对于”new
C”这样的语句,其实相当于operator new(sizeof(C) )后面再跟上C的构造函数,而”C
c1”则是在当前栈上腾出sizeof(C)大小的空间,然后调用C的构造函数。因此,每次X类作了改动,使用c.cc的源文件都必须重新编译一次,因为X的大小可能改变了。
在一个大型的项目中,这种耦合可能会对build时间产生相当大的影响。
pImpl惯用手法可以将这种耦合消除,使用pImpl惯用手法的代码像这样:
//c.cc
class X; //用前导声明取代include
class C
{
...
private:
X* pImpl; //声明一个X*的时候,class X不用完全定义
};
在一个既定平台上,任何指针的大小都是相同的。之所以分为X*,Y*这些各种各样的指针,主要是提供一个高层的抽象语义,即该指针到底指向的是那个类的对象,并且,也给编译器一个指示,从而能够正确的对用户进行的操作(如调用X的成员函数)决议并检查。但是,如果从运行期的角度来说,每种指针都只不过是个32位的长整型(如果在64位机器上则是64位,根据当前硬件而定)。
正由于pImpl是个指针,所以这里X的二进制信息(sizeof(C)等)不会被耦合到C的使用接口上去,也就是说,当用户”new
C”或”C c1”的时候,编译器生成的代码中不会掺杂X的任何信息,并且当用户使用C的时候,使用的是C的接口,也与X无关,从而X被这个指针彻底的与用户隔绝开来。只有C知道并能够操作pImpl成员指向的X对象。
测试用例:
//cls.h
#ifndef _CLS_H #define _CLS_H #include<iostream> #include<memory> class CMyClass { public: CMyClass(int i = 0); ~CMyClass(void); public: void Print(); private: int m_iData; }; #endif
//c.cc
#include"cls.h" CMyClass::CMyClass(int i):m_iData(i) { } CMyClass::~CMyClass(void) { } void CMyClass::Print() { std::cout << "CMyClass::print "<< m_iData <<std::endl; }
//pmipl.h
#ifndef _PMIMPL_ #define _PMIMPL_ #include<iostream> #include<memory> class CMyClass; class CPimpl { public: CPimpl(int i = 0); ~CPimpl(void); public: void Print(); private: std::auto_ptr<CMyClass> ptr_mCls; }; #endif
//pmiph.cc
#include "pimpl.h" #include"cls.h" CPimpl::CPimpl(int i):ptr_mCls(new CMyClass(i)) { } CPimpl::~CPimpl(void) { } void CPimpl::Print() { ptr_mCls->Print(); }
//main.cc
#include "pimpl.h" int main(int argc,char *argv[]) { CPimpl pl(8); pl.Print(); }
相关文章推荐
- PIMPL 模式的实现及应用。
- PIMPL 模式的实现及应用。
- PIMPL 模式的实现及应用。
- 一种互联网应用的分布式架构模式微服务应用框架的实现(gradle,dubbo,zookeeper,springmmvc)
- handler加线程模式实现android应用的异步加载
- 设计模式的应用-策略模式实现支付方式回调策略
- iOS应用中通过设置VOIP模式实现休眠状态下socket的长连接
- 大数据架构和模式(五)对大数据问题应用解决方案模式并选择实现它的产品
- iOS应用中通过设置VOIP模式实现休眠状态下socket的长连接
- AC自动机应用 多模式匹配 多个单词在文章中出现的次数-C语言实现
- 设计模式用过哪些,应用场景是什么;单例模式有几种实现方式,代码怎么写?
- 设计模式之——单例模式(Singleton)的实现、优缺点和常见应用场景
- UML和模式应用-领域模型和用例实现
- 大数据架构和模式(五)——对大数据问题应用解决方案模式并选择实现它的产品
- 大数据架构和模式(五)对大数据问题应用解决方案模式并选择实现它的产品
- 大数据架构和模式(四)了解用于大数据解决方案的原子模式和复合模式 大数据架构和模式(五)对大数据问题应用解决方案模式并选择实现它的产品
- Spring容器装饰者模式应用之实现业务类与服务类自由组合的解决方案
- HDR 拍照模式的原理,实现及应用
- 外观模式及其在简单的学生资格审查系统中的应用(C++实现)
- HDR 拍照模式的原理,实现及应用