![]() ![]() |
|
C++箴言:最小化文件之间的编译依赖 | |
作者:佚名 文章来源:不详 点击数 更新时间:2008/4/18 16:05:50 文章录入:杜斌 责任编辑:杜斌 | |
|
|
问题在于 C++ 没有做好从实现中剥离接口的工作。一个类定义不仅指定了一个类的接口而且有相当数量的实现细节。例如: 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; // implementation detail Date theBirthDate; // implementation detail Address theAddress; // implementation detail }; 在这里,如果不访问 Person 的实现使用到的类,也就是 string,Date 和 Address 的定义,类 Person 就无法编译。这样的定义一般通过 #include 指令提供,所以在定义 Person 类的文件中,你很可能会找到类似这样的东西: #include <string> #include "date.h" #include "address.h" 不幸的是,这样就建立了定义 Person 的文件和这些头文件之间的编译依赖关系。如果这些头文件中的一些发生了变化,或者这些头文件所依赖的文件发生了变化,包含 Person 类的文件和使用了 Person 的文件一样必须重新编译,这样的层叠编译依赖关系为项目带来数不清的麻烦。 你也许想知道 C++ 为什么坚持要将一个类的实现细节放在类定义中。例如,你为什么不能这样定义 Person,单独指定这个类的实现细节呢? namespace std { class string; // forward declaration (an incorrect } // one - see below) class Date; // forward declaration class Address; // forward declaration 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; ... }; 如果这样可行,只有在类的接口发生变化时,Person 的客户才必须重新编译。 这个主意有两个问题。第一个,string 不是一个类,它是一个 typedef (for basic_string<char>)。造成的结果就是,string 的前向声明(forward declaration)是不正确的。正确的前向声明要复杂得多,因为它包括另外的模板。然而,这还不是要紧的,因为你不应该试着手动声明标准库的部件。作为替代,直接使用适当的 #includes 并让它去做。标准头文件不太可能成为编译的瓶颈,特别是在你的构建环境允许你利用预编译头文件时。如果解析标准头文件真的成为一个问题。你也许需要改变你的接口设计,避免使用导致不受欢迎的 #includes 的标准库部件。 第二个(而且更重要的)难点是前向声明的每一件东西必须让编译器在编译期间知道它的对象的大小。考虑: int main() { int x; // define an int Person p( params ); // define a Person ... } |
|
![]() ![]() |