包含保护符

  多个头文件的想法就是把每个逻辑模块表达为一个统一而自给自足的单位。从整个程序的观点看,为使每个逻辑模块完整的那些声明中将有许多是多余的。对于大型程序,这种冗余也可能带来错误,例如将一个包含类定义或者inline函数的头文件两次用#include包含到同一个编译单位里(9.2.3节)。

  我们有两种选择。可以:

1)、重新组织我们的程序,去掉这种冗余。
2)、找到一种允许重复包含头文件的方式。

对于真实规模的程序而言,采用第一种方式---由它得到计算器的最后版本---是很麻烦和不实际的。我们还需要一些冗余,以便使程序中分离的各个孤立部分也容易理解。

  对多余的#include的分析和所产生的程序简化有可能带来许多益处,无论是从逻辑的观点,还是编译时间的缩短。然而这种工作却很少可能做完全,所以还需要使用一些能允许多余的#include的方法。这种方法最好是能够系统化地使用,因为没有办法知道用户认为有价值的分析能做得多么彻底。

  传统的解决方案是在头文件里插入包含保护符。例如,

    // error.h:

    #ifndef CALC_ERROR_H
    #define CALC_ERROR_H

    namespace Error {
        // ...
    }

    #endif              // CALC_ERROR_H

如果CALC_ERROR_H被定义,位于#ifndef和#endif之间的文件内容就会被忽略掉。这样,当error.h在一次编译中被第一次看到时,它的内容将被读入,而CALC_ERROR_H也将被给定一个值。如果在这次编译中error.h被又一次提供给编译器,它的内容就会被忽略。这是一种黑客式的宏手段,但它很有效,在C和C++世界里使用广泛。标准头文件通常也带有包含保护符。

  头文件可能被包含到任意的环境里,而且也没有名字空间保护来抵御宏名字之间的冲突。因此,我总选择相当长的非常难看的名字作为我的包含保护符。

  一旦人们习惯于头文件和包含保护符,他们往往就会倾向于直接或间接地包含大量头文件。即使是使用某些优化了头文件处理功能的C++实现,我们也不应该这样做。因为这将导致不必要的过长的编译时间,而且会把大量声明和宏带进作用域中。后一种现象可能以某些无法预计的有害方式影响程序的语义。应该只包含必要的头文件。

🔚