C++包含“.h”文件,函数重复混淆

4
我正在编写一个程序,但是我不知道为什么会出现错误(注意:我已经解决了这个问题,我只是想知道为什么会出现这个错误以及这意味着包含.h文件的含义)。基本上,我的程序结构如下:
我正在处理的当前文件称为Current.cc(它是Current.h的实现)。
Current.cc包含一个名为CalledByCurrent.h的头文件(它有一个关联的实现叫做CalledByCurrent.cc)。CalledByCurrent.h包含一个类定义。
CalledByCurrent.cc中定义了一个非类函数,名为thisFunction()。由于它实际上不是类的成员函数(只是一个小助手函数),因此在CalledByCurrent.h中没有声明thisFunction()。在Current.cc中,我需要使用这个函数,所以我只是在Current.cc的顶部重新定义了thisFunction()。然而,当我这样做时,我得到了一个错误,说这个函数被重复定义了。为什么会这样,myFunction()甚至没有在CalledByCurrent.h中声明?
因此,我只是从Current.cc中删除了这个函数,现在假设Current.cc可以从CalledByCurrent.cc中访问thisFunction()。然而,当我这样做时,我发现Current.cc不知道我在说什么函数。这是怎么回事?然后,我将thisFunction()函数定义复制到了我的CalledByCurrent.h文件的顶部,这解决了问题。你能帮我理解这种行为吗?特别是,为什么它会认为有一个重复,但却不知道如何使用原始函数?
附言:对于这篇文章的混乱,我感到抱歉。如果有什么需要澄清的,请告诉我。

简化您的帖子的好方法是发布小样本文件,显示“Current.cc”、“CalledByCurrent.cc”及其各自的头文件的内容。 - VinGarcia
6个回答

4
您正在从链接器获取多个定义 - 它看到两个具有相同名称的函数并抱怨。例如:
// a.cpp
void f() {}

// b.cpp
void f() {}

那么

g++ a.cpp b.cpp

提供:

C:\Users\neilb\Temp\ccZU9pkv.o:b.cpp:(.text+0x0): multiple definition of `f()'

这个问题的解决方法是将定义放在一个.cpp文件中,或者将其中一个或两个函数声明为static:
// b.cpp
static void f() {}

但是为什么它会看到两个同名函数,当myFunction()只包含在CalledByCurrent.cc中而不是.h文件中?此外,为什么当我从Current.cc中删除重复项时,它不能使用CalledByCurrent.cc中的函数? - Casey Patton
@The C Man 因为在文件之间解析名称是链接器的工作。 - user2100815
因此,链接器只检查名称而不是可用性?因此,尽管Current.cc实际上无法使用该函数,链接器仍可能将其视为重复项? - Casey Patton
好的,可以通过添加声明来使用它。但是是的,链接器只检查名称。 - user2100815
谢谢你的帮助。我非常喜欢你的回答(并给你点赞),但我必须选择Asha的答案。他/她的回答因为对“全局函数”的解释而更加理想。 - Casey Patton

3
您不能使用相同名称在两个全局函数中(即使是在2个不同的翻译单元中)。为了避免出现链接器错误,请将该函数定义为static,使其在翻译单元之外不可见。 编辑 您可以通过使用extern关键字在其他.cpp文件中使用该函数。请参阅以下示例:
//Test.cpp

void myfunc()
{

}

//Main.cpp
extern void myfunc();
int main()
{
    myfunc();
}

它将调用test.cpp中定义的myfunc()函数。


Neil Butterworth的答案在这个网址中有一个例子:https://dev59.com/V1fUa4cB1Zd3GeqPGURD#6027412。 - Nathan Fellman
谢谢。我理解为什么会出现重复错误。但我不明白的是,当我从Current.cc中删除重复项时,为什么我无法使用thisFunction()(尽管重新声明它会导致重复错误)。 - Casey Patton
@Casey P:你可以使用 extern 来实现。请参见编辑。 - Asha
1
“extern” 实际上并不是必需的,因为函数默认具有 “extern” 链接。 - user2100815

1

这是因为每当您仅声明一个函数时,它被视为在extern(全局)范围内(无论您是否在头文件中声明它)。链接器将具有相同函数签名的多个实现。

如果这些函数确实是辅助函数,则将它们声明为;

static void thisFunction();

如果您正在使用与助手相同的函数,则可以通过在公共头文件中声明它来实现。例如:
//CalledByCurrent.h  (is included in both .cc files)
void thisFunction();

在任何一个 .cc 文件中实现 thisFunction()。这样可以正确解决问题。


1

头文件包含机制应该对重复的头文件包含具有容忍性。


这是正确的,但上述错误与多个头文件包含无关。 - Asha

0

以下是一些建议:

  • 您的头文件中没有放置头文件包含保护。如果被包含了两次,可能会出现这种错误。
  • 函数原型(在顶部)与其签名不完全匹配。
  • 您将函数放在了头文件中。
  • 您在两个不同的源文件中有两个相同签名的函数,但它们未被标记为static

如果您正在使用gcc(您没有说您正在使用哪个编译器),则可以使用-E开关查看预处理器输出。这包括展开所有的#define和包含所有的#include
每次展开时,它都会告诉您它在哪个文件和行中。使用这个方法,您可以看到thisFunction()的定义位置。

即使没有头文件保护,这个错误仍然会出现。你可以尝试在两个不同的cpp文件中编写一个空函数,并将它们一起编译。 - iammilind
没错。我会编辑我的答案以反映这种可能性。 - Nathan Fellman

0

在构建的两个不同阶段中,有2个明显的错误。

在第一种情况下,当您有一个重复项时,编译器很高兴,但链接器会抱怨,因为当它从不同的源文件中获取所有函数定义时,它会注意到有2个名称相同。正如其他答案所述,您可以使用static关键字或使用公共定义。

在第二种情况下,当您看到此范围内未声明您的函数时,这是因为编译器正在抱怨,因为每个文件都需要知道它可以使用哪些函数。

编译发生在链接之前,因此编译器无法预先知道链接器是否会找到匹配的函数,这就是为什么要使用声明来通知编译器稍后链接器将找到定义的原因。

正如您所看到的,您的2个错误并不矛盾,它们是构建过程中具有特定顺序的2个单独的过程的结果。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接