博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
C++实现之——降低文件间的编译依存关系(未完待续)
阅读量:5311 次
发布时间:2019-06-14

本文共 2479 字,大约阅读时间需要 8 分钟。

1)文件间存在的编译依存关系

在C++中,如果没有将接口和实现分离,那么在修改起一个成分,就有可能导致编译时许多文件的重新编译,如定义类如下:

1 class Person 2 { 3 public:  4      Person(const string& name, const Date& birthday, const Address& addr); 5      string name() const; 6      string birthDate() const; 7      string address() const; 8 private: 9      string theName();10      Date theBirthDate;11      Address theAddress;12 };
View Code

由于Person类中使用到了string,Date和Address的定义式,因此需要#include一些头文件,即在文件的开始处需要下面的指令:

1 #include
2 #include "Date.h"3 #include "Birthday.h"
View Code

这样一来,如果这些头文件或者这些头文件所依赖的其他头文件有任何的改变,那么任何包含Person或任何使用Person的文件都需要重新编译,从而形成连串编译依存性,给项目带来灾难。

2)降低文件间的编译依存关系

针对上面的问题 ,也许有程序员会这样设想:将实现细目于类的定义分开,即:

1 namespace std 2 { 3 class string; 4 } 5 class Date; 6 class Address; 7 class Person 8 { 9 public:10 Person(const string& name, const Date& birthday,const Address& addr);11 string name() const;12 string birthDate() const;13 string address() const;14 };
View Code

这样做的话,只需要在Person的接口发生改变时才需要重新编译。但上面的代码不会通过编译,原因有两个:第一string不是个class,而是typedef ,真正的前置声明比较复杂;第二 编译器在编译期间必须知道对象的大小从而分配足够的空间来存放对象,按照上面的定义方法,编译器不知道该为Person分配多少的内存空间。

如果采用类似Java和Smalltalk的方式:编译器在编译期间不需要知道类的大小,只需要分配一个指针大小的空间来指向对象,也即在C++中将类中的实现细节隐藏在一个指针的背后,那么就可以将接口和实现分离,降低编译依存性。具体的讲,就是将Person类分割为两个类,一个只提供接口,一个用来实现接口,如果定义这个实现接口的类为PersonImpl,那么Person的定义如下:

1 #include
2 #include
3 4 class PersonImpl; 5 class Date; 6 class Address; 7 8 class Person 9 {10 public:11 Person(const string& name, const Date& birthday, const Address addr);12 string name() const;13 string birthDate() const;14 string address() const;15 ...16 private:17 tr1::shared_ptr
pImpl;18 };
View Code

在这种设计之下,Person的客户就完全与Dates, Address以及Person的实现细目分离了,这些classes的任何修改都不需要Person客户端重新编译。此外,由于客户无法看到Person的实现细目,也就不能写出“取决于这些细目”的代码,从而真正实现“接口与实现分离”。

上面分离实现的关键是将“声明的依存性”替换“定义的依存性”,这正是编译依存性最小化的本质:现实中让头文件尽可能自我满足,如果做不到,则让它与其他文件内的声明式而非定义式相依。具体的讲:

a. 如果使用object references 或object pointers可以完成任务,就不要使用objects.你可以只靠一个类型声明式就定义出指向该类型的references和pointers;但如果定义某类型的objects,就需要用到该类型的定义式。

b. 如果能够,尽量以class声明式替换class定义式。通常当你声明一个函数而它用到某个class时,你并不需要该class的定义,如:

1 class Date;2 Date today();3 void clearAppointments(Date d);
View Code

实际调用这两个函数时,Date的定义需要先出现,这样看来似乎用class声明代替class定义是多余的,但事实并非如此。因为对于这个函数,并非每个用户都会调用,对于那些不调用这样的函数的用户,用class声明替换class定义很现任可以降低编译依存性。

c. 为声明式和定义式提供不同的头文件,这样一来就引入了handle class 和 interface class.

以上整理自Effective C++中文版第三版case 31.

未完待续。

 

转载于:https://www.cnblogs.com/sophia-yun/archive/2013/06/04/3116747.html

你可能感兴趣的文章
C、JAVA存储管理不同点
查看>>
课后作业-阅读任务-阅读提问-2
查看>>
rtmp服务器以及rtmp推流/拉流/转发
查看>>
面向对象设计中private,public,protected的访问控制原则及静态代码块的初始化顺序...
查看>>
挑战常规--不要这样使用异常
查看>>
malloc函数的用法
查看>>
渐变的参数
查看>>
C#委托详解(3):委托的实现方式大全(续)
查看>>
RaceWeb终于可以在oracle中快速建表了
查看>>
cookie,sessionStorage,localStorage
查看>>
Noip2011提高组总结
查看>>
Java异常之try,catch,finally,throw,throws
查看>>
重载操作符‘==’ , ‘type()’ , ‘+’
查看>>
怎么解决dorado跳转到spring mvc乱码的问题
查看>>
[通信] C#多线程Socket-文件传输
查看>>
强盗分宝石
查看>>
JQuery获取元素的方法总结
查看>>
Android学习路线总结,绝对干货
查看>>
CMFCShellList和自定义ShellList结合使用,达到“直接浏览缩略图,双击打开图片”...
查看>>
JS中如何判断null、undefined与NaN
查看>>