5.6.2 减少重复编译
在我们的编程环境中,当一个文件被修改,或它所依赖的头文件被修改时,项目管理员需要重复编译该文件。这意味着程序员无论何时修改了一个类,无论修改的是公共的接口部分,还是私有成员的声明部分,他都必须再次编译包含头文件的所有文件。这就是通常所说的易碎的基类问题(fragile base-class pr oblem)。对于一个大的项目而言,在开发初期这可能非常难以处理,因为内部实现部分可能需要经常改动。如果这个项目非常大,用于编译的时间过多可能妨碍项目的快速转型。
解决这个问题的技术有时称为句柄类(handle class)或称为“Cheshire cat”[1]。有关实现的任何东西都消失了,只剩一个单指针“smile”。该指针指向一个结构,该结构的定义与其所有的成员函数的定义一同出现在实现文件中。这样,只要接口部分不改变,头文件就不需变动。而实现部分可以按需要任意更改,完成后只需要对实现文件进行重新编译,然后重新连接到项目中。
这里有一个说明这一技术的简单例子。头文件中只包含公共的接口和一个单指针,该单指针指向一个没有完全定义的类。
这是所有客户程序员都能看到的。下面这行
是一个不完全的类型说明(incomplete type specification)或类声明(class declaration)[类定义(class definition)包含类的主体]。它告诉编译器,Cheshire是一个结构名,但没有提供有关该struct的任何细节。这些信息对产生一个指向struct的指针来说已经足够了。但在提供了一个结构的主体部分之前不能创建一个对象。在这种技术里,包含具体实现的结构体被隐藏在实现文件中。
Cheshire是一个嵌套结构,所以它必须用作用域符定义:
在Handle:initialize()中,为Cheshire结构分配存储空间,在Handle:cleanup()中,释放这些存储空间。这些内存被用来代替类的所有private部分。当编译Handle.cpp时,这个结构的定义被隐藏在目标文件中,没有人能看到它。如果改变了Cheshire的组成,惟一要重新编译的是Handle.cpp,因为头文件并没有改动。
Handle的使用就像任何类的使用一样,包含头文件、创建对象、发送消息。
客户程序员惟一能访问的就是公共的接口部分,因此,如果只修改了在实现中的部分,上面文件就不须重新编译。虽然这并不是完全对实现进行了隐藏,但毕竟是一大改进。
[1]这个名字应该归功于John Carolan,他是C++语言早期的先驱之一,当然,还有Lewis Carroll(作家,《爱丽丝奇遇记》的作者—编辑注)。可以把该技术看做本书第2卷中论述的一种“桥”(bridge)设计模式。