C++ 如何找到函数的多个定义

C++ 如何找到函数的多个定义,c++,qt,linker,C++,Qt,Linker,我写了一个findDialog来查找搜索的文本。当我发出make命令时,它返回 g++ -Wl,-O1 -o findDialog FindDialog.o main.o moc_FindDialog.o -L/usr/lib -lQtGui -lQtCore -lpthread moc_FindDialog.o: In function `FindDialog::findClicked()': moc_FindDialog.cpp:(.text+0x20): multiple defi

我写了一个findDialog来查找搜索的文本。当我发出
make
命令时,它返回

g++ -Wl,-O1 -o findDialog FindDialog.o main.o moc_FindDialog.o    -L/usr/lib -lQtGui -lQtCore -lpthread 
moc_FindDialog.o: In function `FindDialog::findClicked()':
moc_FindDialog.cpp:(.text+0x20): multiple definition of `FindDialog::findClicked()'
FindDialog.o:FindDialog.cpp:(.text+0x30): first defined here
moc_FindDialog.o: In function `FindDialog::enableFindButton(QString const&)':
moc_FindDialog.cpp:(.text+0x50): multiple definition of `FindDialog::enableFindButton(QString const&)'
FindDialog.o:FindDialog.cpp:(.text+0x0): first defined here
collect2: ld returned 1 exit status
make: *** [findDialog] Error 1
我已经搜索了这个问题好几个小时了,但我不明白这个问题是由什么引起的。
什么会导致多个定义的错误?

当方法定义包含在多个翻译单元(也称为目标文件)中时,通常会发生这种情况。后来,当链接器合并这些对象文件时,它发现同一个方法有多个定义,并且会抱怨,因为它不知道使用哪一个。下面是如何引入此错误的简单示例:

使头文件
header.hpp
同时包含方法声明及其定义:

class foo {
public:
  void bar ();
};

void foo::bar ()
{
}
并且有两个源文件source1.cpp和source2.cpp,都包含该文件:

source1.cpp

#include "header1.hpp"
int example1()
{
  foo f;
  f.bar ();
}
#include "header1.hpp"
int main ()
{
  foo f;
  f.bar ();
  f.bar ();
}
class foo {
public:
  void bar ();
};
#include "header1.hpp"
void foo::bar ()
{
}
。。。和
source2.cpp

#include "header1.hpp"
int example1()
{
  foo f;
  f.bar ();
}
#include "header1.hpp"
int main ()
{
  foo f;
  f.bar ();
  f.bar ();
}
class foo {
public:
  void bar ();
};
#include "header1.hpp"
void foo::bar ()
{
}
然后,分别编译两个文件并将它们链接在一起。例如:

g++ -c source1.cpp source1.o
g++ -c source2.cpp source2.o
g++ -o a.out source1.o source2.o
class foo {
public:
  void bar ();
};

inline void foo::bar ()
{
}
这将给您一个您在问题中描述的链接器错误,因为方法
foo::bar
在source1和source2对象中出现两次。链接器不知道使用哪一个

此问题有两种常见的解决方案:

解决方案#1-将该方法内联

通过使用
inline
关键字声明该方法,编译器将内联整个方法,或者,如果它决定不内联,它将生成匿名方法(相同的方法,但具有给定对象文件的某个唯一名称),因此对象文件中不会有冲突。例如:

g++ -c source1.cpp source1.o
g++ -c source2.cpp source2.o
g++ -o a.out source1.o source2.o
class foo {
public:
  void bar ();
};

inline void foo::bar ()
{
}
解决方案#2-在另一个源文件中定义(实现)该方法,以便它在整个程序中只出现一次。例如:

g++ -c source1.cpp source1.o
g++ -c source2.cpp source2.o
g++ -o a.out source1.o source2.o
class foo {
public:
  void bar ();
};

inline void foo::bar ()
{
}
header1.hpp

#include "header1.hpp"
int example1()
{
  foo f;
  f.bar ();
}
#include "header1.hpp"
int main ()
{
  foo f;
  f.bar ();
  f.bar ();
}
class foo {
public:
  void bar ();
};
#include "header1.hpp"
void foo::bar ()
{
}
header1.cpp

#include "header1.hpp"
int example1()
{
  foo f;
  f.bar ();
}
#include "header1.hpp"
int main ()
{
  foo f;
  f.bar ();
  f.bar ();
}
class foo {
public:
  void bar ();
};
#include "header1.hpp"
void foo::bar ()
{
}
要决定是否内联,您必须知道(或至少猜测)调用此函数是否比在整个程序中复制/内联此代码更昂贵。内联代码通常会使程序变大并增加编译时间。但这并不一定能让它更快。另外,在源文件中具有定义不会导致使用该函数重新编译所有源文件,而只会重新编译具有定义的源文件,然后重新链接。许多程序员对C++内联感到疯狂,却不真正理解它如何影响程序。我建议使用源文件中的定义,仅当调用该函数成为性能瓶颈时才将其内联,否则内联将修复它


希望能有帮助。快乐编码

在头文件中写入函数定义,然后在项目中的多个
.cpp
中包含这些头文件。与普遍的误解相反,头部防护装置并不能保护你免受此伤害

仅在标题中写入以下内容:

  • 类定义
    • 可能包括成员函数的内联定义
  • 非成员变量声明
  • 非成员函数声明
  • 模板/内联函数定义

将所有其他内容都放在“源文件”中。

从错误消息中的文件名判断,您有一些模拟测试装置作为部分,还有一些真实代码。链接器确实很清楚地报告了问题所在-为了更加清晰,我在这里重新格式化了信息,删除了部分信息:

moc_FindDialog.cpp: multiple definition of `FindDialog::findClicked()'
FindDialog.cpp:     first defined here

moc_FindDialog.cpp: multiple definition of `FindDialog::enableFindButton(QString const&)'
FindDialog.cpp:     first defined here
这清楚地表明链接器首先在
moc_FindDialog.cpp
中遇到了每个函数定义,然后在
FindDialog.cpp
中遇到了第二个定义


我认为您需要仔细查看
moc_FindDialog.cpp
中的模拟测试夹具,并确定为什么要复制这两个函数。或者,您不应该在一个程序中同时链接模拟函数和实际函数——这两个源文件根本不打算链接到一个程序中。

Vlad Lazarenko的答案很好

我只是添加了我在使用QT/C++时遇到的另一种可能性:


当您声明插槽时,不需要编写定义(实现),否则,您将得到一个错误:multiple definition

您可以提供任何源代码吗?谢谢您的回复。原因是自动生成的moc_FindDialog.cpp包含我在FindDialog.cpp文件中编写的函数。当我删除这些函数时,它起作用了。但有一件事我不明白,为什么Qt写了我已经写过的这些函数。嗯,我真的不知道为什么moc生成的源代码包括您的其他源文件。这可能是因为您试图覆盖插槽,而不是连接插槽,或者诸如此类的事情。是的,您关于标题保护的看法是正确的,普遍建议使用标题保护来解决此问题。然而,它们并没有帮助。@stedkka:Header-guards解决了一个微妙的不同问题(一般来说,是多声明而不是定义)。