摘要
各司其责:应该确保所编写的每个头文件都能够独自进行编译,为此需要包含其内容所依赖的所有头文件。
讨论
如果一个文件包含某个头文件时,还要包含另一个头文件才能工作的话,就会增加交流障碍,给头文件的用户增添不必要的负担。
多年前,有些专家曾建议头文件不应该包含其他头文件,因为多次打开和分析带包含保护符(#include guard)的头文件会增加开销。幸运的是,这基本上已经过时了,许多现代的C++编译器能够自动识别头文件保护符(见第24条),甚至不会两次打开同一个头文件。有些编译器还提供了预编译的头文件,有助于确保不会经常分析那些常用而且很少变化的头文件。
但是,不要包含并不需要的头文件,它们只会带来零乱的依赖性。
可以考虑采用这种有助于使头文件自给自足的技术:构建时,独立编译每个头文件,并确认没有产生错误或者警告。
示例
在些更微妙的问题,其成因与模板在关。
例1 非独立名称。模板是在其定义处编译的,但是有一个例外:非独立名称或者类型要等到模板实例化时才编译。这意味着带有一个std::deque<T>成员的template<class T> class Widget,即使没有包含<deque>也不会引发编译时错误,只要没有人实例化Widget。如果Widget必然会被实例化,则头文件中显然应该加入这样的语句:#include <deque>。
例2 只在使用时才实例化成员函数模板和模板的成员函数。假设Widget没有类型为 std::deque<T>的成员,但是它的Transmogrify成员函数使用了deque。那么,Widget的调用者就可以正常实例化和使用Widget,即使没有包含<deque>,但前提是调用者没有使用Transmogrify。默认时,Widget头文件仍然应该包含<deque>,因为这至少对于某些Widget的调用者是必要的。在一些很罕见的情况下,为了很少使用的模板函数,需要包含一个开销很大的头文件,这时应该考虑将这些函数重构为非成员函数,放在一个单独的头文件中,并在该头文件中包含大开销的头文件(见第14条)。





