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

时间:2013-01-20 14:03:34

标签: c++

链接器报告重复符号:

#ifndef testttt
#define testttt

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

#endif

因为它在包含防护中,我希望这个函数只定义一次。但显然不是。

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

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

3 个答案:

答案 0 :(得分:49)

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

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

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

因为static关键字为每个翻译单元制作了该函数的私有副本。

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

答案 1 :(得分:42)

  

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

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

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

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

      

    为什么静态词在静态时会阻止名称在其他翻译单元中显示?

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


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

    1. 将该功能标记为inline
    2. 将该功能标记为static
    3. 将该函数放入未命名的命名空间。
    4. 请注意,#1#2的行为与上面第二个回答中提到的相同 使用#3标准可以放宽内联函数的ODR,并允许每个翻译单元都有自己的定义(提供所有定义相同)。

      因此,如果您真的想在标题#1中添加函数定义是正确的方法。

答案 2 :(得分:5)

  

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

包含防护措施可防止在相同翻译单元中多次包含标题。它确实,但是,防止多个定义:如果标题包含在多个翻译单元中,那么将存在多个定义错误,因为该函数在每个翻译单元中定义,并且因为它具有外部链接,所有翻译单元都可以看到所有其他翻译单元的定义。要防止出现此错误,您只需在标头中提供声明,并在一个.cpp文件中提供定义

了解One Definition Rule (ODR)External Linkage

  

2)为什么静态词在静态时会阻止名字在其他翻译单元中的可见性?

因为static使函数内部到每个翻译单元。这就是内部链接意味着:其他翻译单元看不到定义。