读书笔记_Effective_C++_条款三十一:将文件间的编译依存关系降至最低(第三部分)
下面来谈谈书中的第二部分,用Interface Classes来降低编译的依赖。从上面也可以看出,避免重编的诀窍就是保持头文件(接口)不变化,而保持接口不变化的诀窍就是不在里面声明编译器需要知道大小的变量,Handler Classes的处理就是把变量换成变量的地址(指针),头文件只有class xxx的声明,而在cpp里面才包含xxx的头文件。Interface Classes则是利用继承关系和多态的特性,在父类里面只包含成员方法(成员函数),而没有成员变量,像这样:
// Person.h
#include <string>
using namespace std; class MyAddress;
class MyDate;
class RealPerson; class Person
{
public:
virtual string GetName() const = ;
virtual string GetBirthday() const = ;
virtual string GetAddress() const = ;
virtual ~Person(){}
};
而这些方法的实现放在其子类中,像这样:
// RealPerson.h
#include "Person.h"
#include "MyAddress.h"
#include "MyDate.h" class RealPerson: public Person
{
private:
string Name;
MyAddress Address;
MyDate Birthday;
public:
RealPerson(string name, const MyAddress& addr, const MyDate& date):Name(name), Address(addr), Birthday(date){}
virtual string GetName() const;
virtual string GetAddress() const;
virtual string GetBirthday() const;
};
在RealPerson.cpp里面去实现GetName()等方法。从这里我们可以看到,只有子类里面才有成员变量,也就是说,如果Address的头文件变化了,那么子类一定会重编,所有用到子类头文件的文件也要重编,所以为了防止重编,应该尽量少用子类的对象。利用多态特性,我们可以使用父类的指针,像这样Person* p = new RealPerson(xxx),然后p->GetName()实际上是调用了子类的GetName()方法。
但这样还有一个问题,就是new RealPerson()这句话一写,就需要RealPerson的构造函数,那么RealPerson的头文件就要暴露了,这样可不行。还是只能用Person的方法,所以我们在Person.h里面加上这个方法:
// Person.h
static Person* CreatePerson(string name, const MyAddress& addr, const MyDate& date);
注意这个方法是静态的(没有虚特性),它被父类和所有子类共有,可以在子类中去实现它:
// RealPerson.cpp
#include “Person.h”
Person* Person::CreatePerson(string name, const MyAddress& addr, const MyDate& date)
{
return new RealPerson(name, addr, date);
}
这样在客户端代码里面,可以这样写:
// Main.h
class MyAddress;
class MyDate;
void ProcessPerson(const string& name, const MyAddress& addr, const MyDate& date);
// Main.cpp
#include "Person.h"
#include “MyAddress.h”;
#include “MyDate.h”; void ProcessPerson(const string& name, const MyAddress& addr, const MyDate& date)
{
Person* p = Person::CreatePerson(name, addr, date);
…
}
就可以减少编译依赖了。
总结一下,Handler classes与Interface classes解除了接口和实现之间的耦合关系,从而降低文件间的编译依存性。减少编译依存性的关键在于保持.h文件不变化,具体地说,是保持被大量使用的类的.h文件不变化,这里谈到了两个方法:Handler classes与Interface classes。
Handler classes化类的成员变量为指针,在.h文件里面只包含class xxx的外来类声明,而不包含其头文件,在.cpp涉及到具体外来类的使用时,才包含xxx.h的头文件,这样最多只影响本身类的cpp重编,但因为.h文件没有变化,所以此类的对象存在的文件不必重编。
当然,书上说的Handler classes更想让我们在类A的基础上另造一个中间类AImp(成员函数完全与类A一致),这个中间类的成员中里面放置了所有类A需要的外来类的对象,然后类的逻辑细节完全在Almp.cpp中实现,而在A.cpp里面只是去调用Almp.cpp的同名方法。A.h的成员变量只有Almp的指针,这看上去好像一个Handler,因此而得名。
Interface classes则是将细节放在子类中,父类只是包含虚方法和一个静态的Create函数声明,子类将虚方法实现,并实现Create接口。利用多态特性,在客户端只需要使用到Person的引用或者指针,就可以访问到子类的方法。由于父类的头文件里面不包含任何成员变量,所以不会导致重编(其实由于父类是虚基类,不能构造其对象,所以也不用担心由于父类头文件变化导致的重编问题)。
请记住:
1. 支持“编译依存性最小化”的一般构想是:相依于声明式,不要相依于定义式,基于此构想的两个手段是Handler classes和Interface classes。
2. 程序库头文件应该以“完全且仅有声明式”的形式存在,这种做法不论是否涉及templates都适用。
读书笔记_Effective_C++_条款三十一:将文件间的编译依存关系降至最低(第三部分)的更多相关文章
- Effective C++ -----条款31:将文件间的编译依存关系降至最低
支持“编译依存性最小化”的一般构想是:相依于声明式,不要相依于定义式.基于此构想的两个手段是Handle classes 和 Interface classes. 程序库头文件应该以“完全且仅有声明式 ...
- 条款31:将文件间的编译依存关系降至最低(Minimize compilation dependencies between files)
NOTE1: 1.支持“编译依存性最小化”的一般构想是:相依于声明式,不要相依于定义式.基于此构想的两个手段是Handle classes 和 Interface classes. 2.程序库头文件应 ...
- 读书笔记_Effective_C++_条款四十一:了解隐式接口和编译期多态
从本条款开始,就进入了全书的第七部分:模板与泛型编程.模板与泛型在C++中是非常重要的部分,还记得本书第一章时,把C++视为一个联邦,它由四个州政府组成,其中一个政府就是模板与泛型了. 本条款是一个介 ...
- 读书笔记_Effective_C++_条款二十一:当必须返回对象时,别妄想返回其reference
在栈空间的临时成员变量在函数生命期结束后无法传出 friend A& operator*(const A& a, const A& b) { A temp; temp.data ...
- 读书笔记_Effective_C++_条款三十:了解inline的里里外外
学过基本程序课的同学都知道,inline是内联的关键字,它可以建议编译器将函数的每一个调用都用函数本体替换.这是一种以空间换时间的做法.把每一次调用都用本体替换,无疑会使代码膨胀,但可以节省函数调用的 ...
- 读书笔记_Effective_C++_条款二十八:避免返回handlers指向对象内部成分
举个例子: class Student { private: int ID; string name; public: string& GetName() { return name; } } ...
- 读书笔记_Effective_C++_条款三十九:明智而审慎地使用private继承
private继承的意义在于“be implemented in turns of”,这个与上一条款中说的复合模型的第二层含义是相同的,这也意味着通常我们可以在这两种设计方法之间转换,但书上还是更提倡 ...
- 读书笔记_Effective_C++_条款三十四:区分接口继承和实现继承
这个条款书上内容说的篇幅比较多,但其实思想并不复杂.只要能理解三句话即可,第一句话是:纯虚函数只继承接口:第二句话是:虚函数既继承接口,也提供了一份默认实现:第三句话是:普通函数既继承接口,也强制继承 ...
- 读书笔记_Effective_C++_条款三十二:确定你的public继承继承塑模出is-a关系
这一条款是说的是公有继承的逻辑,如果使用继承,而且继承是公有继承的话,一定要确保子类是一种父类(is-a关系).这种逻辑可能与生活中的常理不相符,比如企鹅是生蛋的,所有企鹅是鸟类的一种,直观来看,我们 ...
随机推荐
- Fiddler是最强大最好用的Web调试工具
Fiddler是最强大最好用的Web调试工具之一,它能记录所有客户端和服务器的http和https请求,允许你监视,设置断点,甚至修改输入输出数据. 使用Fiddler无论对开发还是测试来说,都有很大 ...
- poj1077
题意:给出一个八数码问题,求解法,不可解则输出unsolvable. 分析:可以用ida*算法,估价函数可以使用每个数码到其最终位置的最短距离之和.对于不可解的判断,我这里用迭代深度大于100时判定为 ...
- java基础60 JavaScript字符串转换成数字(网页知识)
1.字符串转换成数字 <!doctype html> <html> <head> <meta charset="utf-8"> &l ...
- java基础52 编码与解码
1.解码与编码的含义 编码:把看得懂的字符变成看不懂的码值,这个过程就叫编码 解码:根据码值查到相对应的字符,我们把这个过程就叫解码 注意:编码与解码时,我们一般使用统一的码表,否则非常容易出现 ...
- 1.SpringBoot之Helloword 快速搭建一个web项目
背景: Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程.该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配 ...
- CxGrid 表格标题头居中
选中这些列后 搞.
- Codeforces 552C Vanya and Scales(进制转换+思维)
题目链接:http://codeforces.com/problemset/problem/552/C 题目大意:有101个砝码重量为w^0,w^1,....,w^100和一个重量为m的物体,问能否在 ...
- ZOJ 3781 Paint the Grid Reloaded(DFS连通块缩点+BFS求最短路)
题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=5268 题目大意:字符一样并且相邻的即为连通.每次可翻转一个连通块X( ...
- Qt通过ODBC来操作Excel
示例代码: #include<QtCore/QCoreApplication> #include<QtSql> #include<QObject> #include ...
- Python学习笔记:startswith & endswith 判断开头结尾是否为指定字符串
作用: 判断字符串是否以指定字符或子字符串结尾,常用于判断文件类型. 如果以指定后缀结尾返回True,否则返回False. 可选参数"start"与"end"为 ...