为什么包含守卫不像其他标题项那样阻止此函数的多个定义?
从 C++ 程序创建可执行文件的过程包括三个阶段:
- 预处理
- 编译 &
- 链接
预处理:在此阶段替换宏等预处理器指令。
编译是通过检查语言语义将源代码转换为目标代码。
链接是将所有生成的目标代码链接在一起以形成可执行文件。
标头保护可防止标头的内容在预处理期间多次包含在同一 翻译单元中。它们不会阻止内容包含在不同的翻译单元中。当你在不同的翻译单元中包含这个头文件时,每个单元都会有这个函数的定义。
编译器分别编译每个翻译单元以生成一个单独的目标文件(.o),每个 .o 文件都将具有此函数定义的副本。当链接器在生成时尝试链接到函数定义时,.exe
它会发现相同函数的多个定义,从而导致混淆要链接到哪一个。为了避免这个问题,标准定义了一个规则,称为一个定义规则(ODR),它禁止同一实体的多个定义。
如您所见,在头文件中包含函数定义并将该头文件包含在多个翻译单元中违反了 ODR。
通常的做法是在头文件中提供声明,在一个且只有一个源文件中提供定义。
当静态应该防止名称在其他翻译单元中可见时,为什么静态词会解决这个问题?
当您将关键字添加static
到函数时,每个翻译单元都将拥有自己的函数副本。默认情况下,函数具有外部链接,但static
强制函数具有内部链接。因此,该定义对不同的翻译单元不可见。这种函数的每个实例都被视为一个单独的函数(每个函数的地址不同),并且这些函数的每个实例都有自己的静态局部变量和字符串文字的副本。请注意,这会大大增加可执行文件的大小。
如果要将函数定义包含在头文件中。有3种方法可以做到:
- 将函数标记为
inline
或
- 将函数标记为
static
或
- 将函数放在未命名的命名空间中。
请注意,#1
并#2
执行与上面第二个答案中提到的相同的操作。
该#3
标准放宽了内联函数的 ODR,并允许每个翻译单元有自己的定义(前提是所有定义都相同)。
因此,如果您真的想将函数定义放在 header#1
中是正确的方法。