读书笔记《Effective C++》条款31:将文件间的编译依存关系降至最低

来源:互联网 发布:卡通农场数据恢复 编辑:程序博客网 时间:2024/04/30 00:41

假设对C++程序的某个class实现文件做了些修改。注意,修改的不是class接口,而是实现,而且只改private成分。然后重新make,却发现整个世界都被重新编译和连接了!

问题出在C++并没有把“将接口从实现中分离”这事做得很好。Class的定义式不只详细叙述了class接口,还包括十足的实现细节。例如:

class Person {public:Person(const std::string& name, const Date& birthday, const Address& addr);std::string name() const;std::string birthDate() const;std::string address() const;private:std::string theName;//实现细节Date theBirthDate;//实现细节Address theAddress;//实现细节};
这里的class Person无法通过编译——如果编译器没有取得其实现代码所用到的class string,Date和Address的定义。这样的定义通常由#define指示符提供,所以Person定义文件的最上方可能存在这样的东西:

#include <string>#include "date.h"#include "address.h"
不幸的是,这么一来便是在Person定义文件和其include文件之间形成了一种编译依存关系。如果这些头文件中有任何一个被改变,或这些头文件所依赖的其它头文件有任何改变,那么每一个包含Person class的文件就得重新编译,任何使用Person class的文件也必须重新编译。这样的连串编译依存关系会对许多项目造成难以形容的灾难。

针对Person我们可以这样做:把Person分割为两个class,一个只提供接口,另一个负责实现该接口。

#include <string>#include <memory>class PersonImpl;//Person实现类的前置声明class Date;//Person接口用到的class的前置声明class Address;class Person {public:Person(const std::string& name, const Date& birthday, const Address& addr);std::string name() const;std::string birthDate() const;std::string address() const;private:        std::shared_ptr<PersonImpl> pImpl;//指针,指向实现物};
在这里,main class(Person)只内含一个指针成员,指向其实现类(PersonImpl)。这般设计常被称为pimpl idiom(pimpl是“pointer to implementation”的缩写)。这种class内的指针名称往往就是pImpl。

这样设计下,Person的客户就完全于Data,Address以及Person的实现细节分离了。那些class的任何实现修改都不需要Person客户端重新编译。此外由于客户无法看到Person的实现细节,也就不可能写出什么“取决于那些细节”的代码。这真正是“接口与实现分离”!

这种分离的关键在于以“声明的依存性”替换“定义的依存性”,那正是编译依存性最小化的本质:现实中让头文件尽可能自我满足,万一做不到,则让它与其他文件内的声明式(而非定义式)相依。其他每一件事都源自于这个简单的设计策略:

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

2.如果能够,尽量以class声明式替换class定义式。注意,当声明一个函数而它用到某个class时,并不需要该class的定义:纵使函数以by value方式传递该类型的参数(或返回值)亦然。

3.为声明式和定义式提供不同的头文件。当然,这些文件必须保持一致。


要点:

1.支持“编译依存性最小化”的一般构想是:相依于声明式,不要相依于定义式。基于此构想的两个手段是Handle classes和Interface classes。

2.程序库头文件应该以“完全且仅有声明式”的形式存在。这种做法不论是否涉及template都适用。

阅读全文
0 0