为什么包含警卫不会阻止多个函数定义?

joh*_*ers 44 c++

链接器报告重复的符号:

#ifndef testttt
#define testttt

void anything(){
    std::cout<<"hellooooooo";
}

#endif
Run Code Online (Sandbox Code Playgroud)

因为它在包含防护内部,所以我希望这个函数只定义一次.但显然不是.

我知道我可以把static它放在它前面然后它会工作(我仍然觉得具有讽刺意味,因为静态应该给它内部链接,但该函数可以从多个cpp文件中使用).

所以我想我的两部分问题是:1)为什么包含警戒不会阻止此功能的多个定义,就像它们对其他标题项一样,2)为什么这个static词在静态应该阻止名称可见时解决这个问题在其他翻译单位?我添加它,我实际上可以从包含此头文件的任何地方调用此函数.

And*_*owl 51

"1)为什么包含警戒不会阻止此函数的多个定义,就像它们对其他标题项一样"

因为每个翻译单元(即.cpp文件)是单独处理的并且经历相同的条件.翻译单元不会共享其他翻译单元遇到的预处理程序定义.这意味着将处理该标头的所有翻译单元都将包含该功能的定义.当然,链接器会抱怨同一个函数有多个定义.

"2)当静态被认为阻止名称在其他翻译单元中可见时,为什么静态词会解决这个问题呢?"

因为static关键字为每个翻译单元制作该功能的私有副本.

但是,如果您希望在共享标头中定义该功能,则应该将其标记为inline,这将解决您的问题并使预处理器保护变得不必要.

  • +1,除了不会“不需要预处理器防护”。守卫使我们免于在同一翻译单元中进行多个定义。 (3认同)

Alo*_*ave 43

为什么包含警戒不会像其他标题项一样阻止此函数的多个定义?

从C++程序创建可执行文件的过程包括三个阶段:

  1. 预处理
  2. 编译和
  3. 链接

预处理:在此阶段替换宏等预处理程序指令.
编译通过检查语言语义将源代码转换为目标代码.
链接是将所有生成的目标代码链接在一起以形成可执行文件.

在预处理期间,标题保护可防止标题的内容在同一 翻译单元中多次包含.它们不会阻止内容包含在不同的翻译单元中.当您将此头文件包含在不同的翻译单元中时,每个单元都将具有此功能的定义.
编译器分别编译每个翻译单元以生成单独的目标文件(.o),每个.o文件都将具有此函数定义的副本.当链接器在生成时尝试链接到函数定义时,.exe它会找到相同函数的多个定义,从而导致混淆哪个链接到哪个.为了避免这个问题,该标准定义了一个称为一个定义规则(ODR)的规则,该规则禁止同一个实体的多个定义.
如您所见,在头文件中包含函数定义,并且在多个翻译单元中包含该头文件违反了ODR.
通常的方法是在头文件中提供声明,并在一个且只有一个源文件中提供定义.

为什么静态词会阻止名称在其他翻译单元中的可见性时解析这个?

将关键字添加static到函数时,每个翻译单元都将拥有自己的函数副本.默认情况下,函数具有外部链接,static强制函数具有内部链接.因此,该定义对于不同的翻译单元是不可见的.这样一个函数的每个实例都被视为一个单独的函数(每个函数的地址不同),这些函数的每个实例都有自己的静态局部变量和字符串文字的副本.请注意,这会大大增加可执行文件的大小.


如果要将函数定义包含在头文件中.有3种方法可以做到:

  1. 将功能标记为inline
  2. 将功能标记为static
  3. 将函数放在未命名的命名空间中.

请注意#1#2执行与上面第二个答案中提到的相同的操作.
使用#3该标准可以放宽ODR以实现内联函数,并允许每个翻译单元都有自己的定义(前提是所有定义都相同).

所以如果你真的想在头文件中放置一个函数定义#1是正确的方法.


Naw*_*waz 5

1)为什么包含保护不会像其他标题项一样阻止此函数的多个定义,

包含防护措施可防止在同一翻译单元中多次包含标头.它并没有,不过,从多个定义防止:如果标题包含在多个翻译单元,那么将有多个定义错误,因为该功能在每个翻译单元定义,因为它具有外部联动,所有的翻译单元可以查看所有其他翻译单元的定义.要防止此错误,您只需在标头中提供声明,并在一个文件中提供定义.cpp.

阅读有关一个定义规则(ODR)外部链接的信息.

2)当静态被认为阻止名称在其他翻译单元中可见时,为什么静态词会解决这个问题?

因为static使每个翻译单元内部都有功能.这就是内部联动意味着:其他翻译单元看不到定义.