多重定义错误c++
multiple definition error c++
我的 'Headers.h' 文件包含基本的 c++ 头文件
#include <iostream>
#include <cstring>
// and many header files.
编写了文件存在检查的函数定义并将其保存在 'common_utility.h' - ifFileExist()
common_utility.h
bool ifFileExist()
{
// ... My code
}
为 Class A 编写了代码
classA.h
class A
{
// Contains class A Declarations.
};
classA.cpp
// Contains
#include "Headers.h"
#include "common_utility.h"
#include "classA.h"
// class A Method definition
为 Class B 编写了代码
我在 Class B.
中使用 class A
classB.h
class B
{
// Contains class A Declarations.
}
classB.cpp
// Contains
#include "Headers.h"
#include "common_utility.h"
#include "classA.h"
#include "classB.h"
// class B Method definition
// calling the function ifFileExist() in class B also.
为主程序编写代码
main.cpp
// Contains
#include "Headers.h"
#include "common_utility.h"
#include "classA.h"
#include "classB.h"
// I am using class A and Class B in main program
// calling the function ifFileExist() in Main program also.
当我将整个程序编译为
g++ -std=c++0x classA.cpp classB.cpp main.cpp -o main
我收到以下错误。
In function ifFileExist()': classB.cpp:(.text+0x0): multiple
definition of
ifFileExist()'
/tmp/ccHkDT11.o:classA.cpp:(.text+0x2b6e): first defined here
所以我将 Headers.h 中的 ifFileExist() 函数清除为外部函数。
extern bool ifFileExist();
但我仍然遇到同样的错误。
我在每个 .cpp 文件中包含 'Headers.h'。该文件包含基本的 C++ 库。但是我没有得到该头文件的任何多重定义错误。
但仅在我自己的函数中,我收到错误 'multiple definition'.
我想在需要时使用 'common_utility.h' 文件。如果我不需要在我的主程序中使用 common_utility 函数,我就不应该包含它。
我希望我的程序在以下每种情况下都运行。
g++ -std=c++0x classA.cpp main.cpp -o main
g++ -std=c++0x classB.cpp> main.cpp -o main
g++ -std=c++0x classA.cpp classB.cpp main.cpp -o main
在任何情况下我都不应该出现多重定义错误。我现在应该怎么办?
在实际编译源代码之前,编译单元是从.cpp 文件生成的。这基本上意味着所有预处理器指令都被计算:所有 #include
将被包含文件的内容替换,所有 #define
的值将被替换为相应的表达式,所有 #if 0 ... #endif
将是删除等。因此,在您的情况下,在这一步之后,您将获得两段没有任何预处理器指令的 C++ 代码,它们都具有相同函数的定义 bool ifFileExist()
,这就是为什么会出现此多重定义错误的原因。
快速解决方案是将其标记为inline bool ifFileExist()
。基本上你要求编译器用函数本身的内容替换所有相应的函数调用。
另一种方法是在 common_utility.h
中保留函数声明并将定义移动到 common_utility.cpp
因为我找不到任何完整的(在我看来)重复这个问题,我打算写一个(希望)权威和完整的答案。
什么是单一定义规则,我为什么要关心
单一定义规则,通常称为 ODR,是一种规定(简化)程序中使用的任何实体(非正式术语)都应定义一次且仅定义一次的规则。多次定义的实体通常会导致编译或 linker 错误,但有时可能会被编译器未检测到并导致非常 hard-to-trace 错误。
这里我不打算正式定义entity,但可以将其视为函数、变量或class。在进一步深入之前,应该非常清楚地了解 C++ 中 definition 和 declaration 之间的区别,因为虽然禁止双重定义,但双重声明通常是不可避免的.
定义与声明
代码中使用的每个实体都应在给定的翻译单元中声明(翻译单元通常是一个 cpp 源文件以及其中包含的所有 header 个文件,直接或间接通过其他 header 个文件)。声明实体的方式因实体本身而异。请参阅下文了解如何声明不同类型的实体。实体通常在 header 文件中声明。由于大多数复杂的应用程序中有多个翻译单元(多个 cpp 文件),并且不同的 cpp 文件通常包含相同的 headers,因此一个应用程序很可能对使用的许多实体有多个声明。就像我上面说的,这不是问题。
应用程序中使用的每个实体都必须定义一次且仅一次。术语 'application' 在这里使用得有点松散 - 例如,库(静态和动态)可以在其中包含未定义的实体(此时通常称为符号),以及 link 的可执行文件使用动态库的 ed 也可以有一个未定义的符号。相反,我指的是应用程序是最终的 运行ning something,在所有库都静态或动态地 linked 到其中,并且符号解析之后。
还值得注意的是,每个定义也可以作为声明,这意味着,每当您定义某物时,您也在声明同一件事。
与声明一样,定义实体的方式因实体类型而异。这是如何 declare/define 3 种基本类型的实体 - 变量、classes 和函数 - 基于它的类型。
变量
使用以下结构声明变量:
extern int x;
这声明了一个变量 x。它没有定义它!以下一段代码将编译正常,但如果没有任何其他输入文件(例如,使用 g++ main.cpp
)尝试 link 它会由于未定义的符号而产生 link-time 错误:
extern int x;
int main() {
return x;
}
下面的一段代码定义了变量x:
int x;
如果将这一行放入文件 x.cpp,并且此文件 compiled/linked 与上面的 main.cpp 和 g++ x.cpp main.cpp -o test
一起,它将编译并且 link没有问题。您甚至可以 运行 生成可执行文件,如果您要在可执行文件 运行 之后检查退出代码,您会注意到它是 0。(因为全局变量 x 将是 default-initialized 到0).
函数
函数是通过提供它们的原型来声明的。典型的函数声明如下所示:
double foo(int x, double y);
此构造声明了一个函数 foo
,返回 double
并接受两个参数 - 一个类型为 int
,另一个类型为 double
。此声明可以出现多次。
下面的代码定义上面提到的foo
:
void foo(int x, double y) {
return x * y;
}
这个定义在整个应用中只能出现一次
函数定义与变量定义相比有一个额外的怪癖。如果将上述 foo
的定义放入 header 文件 foo.h
,这又将包含在两个 cpp 文件 1.cpp
和 2.cpp
中,它们是 compiled/linked 和 g++ 1.cpp 2.cpp -o test
一起你会得到一个 linker 错误,说 foo()
被定义了两次。这可以通过使用以下形式的 foo
声明来防止:
inline void foo(int x, double y) {
return x * y;
}
注意 inline
那里。它告诉编译器的是 foo
可以被多个 .cpp 文件包含,并且这种包含 不应该 产生 linker 错误。编译器有多种选择来实现这一点,但可以依靠它来完成它的工作。请注意,在同一个翻译单元中两次使用此定义仍然是错误的!例如,以下代码将产生编译器错误
inline void foo() { }
inline void foo() { }
值得注意的是,在class中定义的任何class方法都是隐式内联的,例如:
class A {
public:
int foo() { return 42; }
};
这里定义了A::foo()inline
.
类
类已声明y 以下构造:
class X;
上面的声明声明了classX(此时X被正式称为不完整类型),所以当不需要有关其内容的信息(例如大小或成员)时,可以使用它。例如:
X* p; // OK - no information about class X is actually required to define a pointer to it
p->y = 42; // Error - compiler has no idea if X has any member named `y`
void foo(X x); // OK - compiler does not need to generated any code for this
void foo(X x) { } // Error - compiler needs to know the size of X to generate code for foo to properly read it's argument
void bar(X* x) { } // OK - compiler needs not to know specifics of X for this
class 的定义对每个人来说都是 well-known,并遵循以下结构:
class X {
public:
int y;
};
这使得 class X 定义,现在它可以在任何上下文中使用。重要说明 - class 定义必须是每个翻译单元唯一的,但不必是每个应用程序唯一的。也就是说,每个翻译单元只能定义一次 X,但它可以在多个文件中一起使用 linked。
如何正确遵守网上解决规则
只要在生成的应用程序中多次定义同一个实体,就会发生 so-called ODR 违规。大多数时候,linker 会看到违规行为并进行投诉。但是,有些情况下 ODR 违规不会破坏 linking 反而会导致错误。例如,当定义全局变量 X 的同一个 .cpp 文件同时放入应用程序和按需加载的动态库(dlopen
)时,可能会发生这种情况。 (你真的花了几天时间试图追踪一个因此而发生的错误。)
更常见的 ODR 违规原因是:
同一实体在同一范围内的同一文件中定义两次
int x;
int x; // ODR violation
void foo() {
int x;
} // No ODR violation, foo::x is different from x in the global scope
预防:不要这样做。
同一个实体在应该声明的时候定义了两次
(in x.h)
int x;
(in 1.cpp)
#include <x.h>
void set_x(int y) {
x = y;
}
(in 2.cpp)
#include <x.h>
int get_x() {
return x;
}
虽然上面代码的智慧充其量是值得怀疑的,但它可以说明 ODR 规则。在上面的代码中,变量 x 应该在两个文件 1.cpp 和 2.cpp 之间共享,但编码不正确。相反,代码应如下所示:
(in x.h)
extern int x; //declare x
(in x.xpp)
int x; // define x
// 1.cpp and 2.cpp remain the same
预防
知道你在做什么。在需要声明实体时声明它们,不要定义它们。
如果在上面的示例中我们使用函数而不是变量,如下所示:
(in x.h)
int x_func() { return 42; }
我们会遇到一个可以通过两种方式解决的问题(如上所述)。我们可以使用 inline
函数,或者我们可以将定义移动到 cpp 文件中:
(in x.h)
int x_func();
(in x.cpp)
int x_func() { return 42; }
相同的 header 文件包含两次,导致相同的 class 定义两次
这是一个有趣的。想象一下,您有以下代码:
(in a.h)
class A { };
(in main.cpp)
#include <a.h>
#include <a.h> // compilation error!
上面的代码很少像写的那样出现,但是通过中间两次包含同一个文件是很容易的:
(in foo.h)
#include <a.h>
(in main.cpp)
#include <a.h>
#include <foo.h>
Prevention 对此的传统解决方案是使用 so-called include guards,即一个特殊的预处理器定义,它将防止double-inclusion。在这方面,a.h应该重做如下:
(in a.h)
#ifndef INCLUDED_A_H
#define INCLUDED_A_H
class A { };
#endif
上面的代码将防止 a.h 多次包含到同一个翻译单元中,因为 INCLUDED_A_H
将在第一次包含后定义,并且将在所有后续的 #ifndef
中失败一个。
一些编译器公开了其他控制包含的方法,但迄今为止,包含保护仍然是跨不同编译器统一执行此操作的方法。
我的 'Headers.h' 文件包含基本的 c++ 头文件
#include <iostream>
#include <cstring>
// and many header files.
编写了文件存在检查的函数定义并将其保存在 'common_utility.h' - ifFileExist()
common_utility.h
bool ifFileExist()
{
// ... My code
}
为 Class A 编写了代码 classA.h
class A
{
// Contains class A Declarations.
};
classA.cpp
// Contains
#include "Headers.h"
#include "common_utility.h"
#include "classA.h"
// class A Method definition
为 Class B 编写了代码 我在 Class B.
中使用 class AclassB.h
class B
{
// Contains class A Declarations.
}
classB.cpp
// Contains
#include "Headers.h"
#include "common_utility.h"
#include "classA.h"
#include "classB.h"
// class B Method definition
// calling the function ifFileExist() in class B also.
为主程序编写代码 main.cpp
// Contains
#include "Headers.h"
#include "common_utility.h"
#include "classA.h"
#include "classB.h"
// I am using class A and Class B in main program
// calling the function ifFileExist() in Main program also.
当我将整个程序编译为
g++ -std=c++0x classA.cpp classB.cpp main.cpp -o main
我收到以下错误。
In function
ifFileExist()': classB.cpp:(.text+0x0): multiple definition of
ifFileExist()' /tmp/ccHkDT11.o:classA.cpp:(.text+0x2b6e): first defined here
所以我将 Headers.h 中的 ifFileExist() 函数清除为外部函数。
extern bool ifFileExist();
但我仍然遇到同样的错误。
我在每个 .cpp 文件中包含 'Headers.h'。该文件包含基本的 C++ 库。但是我没有得到该头文件的任何多重定义错误。 但仅在我自己的函数中,我收到错误 'multiple definition'.
我想在需要时使用 'common_utility.h' 文件。如果我不需要在我的主程序中使用 common_utility 函数,我就不应该包含它。
我希望我的程序在以下每种情况下都运行。
g++ -std=c++0x classA.cpp main.cpp -o main
g++ -std=c++0x classB.cpp> main.cpp -o main
g++ -std=c++0x classA.cpp classB.cpp main.cpp -o main
在任何情况下我都不应该出现多重定义错误。我现在应该怎么办?
在实际编译源代码之前,编译单元是从.cpp 文件生成的。这基本上意味着所有预处理器指令都被计算:所有 #include
将被包含文件的内容替换,所有 #define
的值将被替换为相应的表达式,所有 #if 0 ... #endif
将是删除等。因此,在您的情况下,在这一步之后,您将获得两段没有任何预处理器指令的 C++ 代码,它们都具有相同函数的定义 bool ifFileExist()
,这就是为什么会出现此多重定义错误的原因。
快速解决方案是将其标记为inline bool ifFileExist()
。基本上你要求编译器用函数本身的内容替换所有相应的函数调用。
另一种方法是在 common_utility.h
中保留函数声明并将定义移动到 common_utility.cpp
因为我找不到任何完整的(在我看来)重复这个问题,我打算写一个(希望)权威和完整的答案。
什么是单一定义规则,我为什么要关心
单一定义规则,通常称为 ODR,是一种规定(简化)程序中使用的任何实体(非正式术语)都应定义一次且仅定义一次的规则。多次定义的实体通常会导致编译或 linker 错误,但有时可能会被编译器未检测到并导致非常 hard-to-trace 错误。
这里我不打算正式定义entity,但可以将其视为函数、变量或class。在进一步深入之前,应该非常清楚地了解 C++ 中 definition 和 declaration 之间的区别,因为虽然禁止双重定义,但双重声明通常是不可避免的.
定义与声明
代码中使用的每个实体都应在给定的翻译单元中声明(翻译单元通常是一个 cpp 源文件以及其中包含的所有 header 个文件,直接或间接通过其他 header 个文件)。声明实体的方式因实体本身而异。请参阅下文了解如何声明不同类型的实体。实体通常在 header 文件中声明。由于大多数复杂的应用程序中有多个翻译单元(多个 cpp 文件),并且不同的 cpp 文件通常包含相同的 headers,因此一个应用程序很可能对使用的许多实体有多个声明。就像我上面说的,这不是问题。
应用程序中使用的每个实体都必须定义一次且仅一次。术语 'application' 在这里使用得有点松散 - 例如,库(静态和动态)可以在其中包含未定义的实体(此时通常称为符号),以及 link 的可执行文件使用动态库的 ed 也可以有一个未定义的符号。相反,我指的是应用程序是最终的 运行ning something,在所有库都静态或动态地 linked 到其中,并且符号解析之后。
还值得注意的是,每个定义也可以作为声明,这意味着,每当您定义某物时,您也在声明同一件事。
与声明一样,定义实体的方式因实体类型而异。这是如何 declare/define 3 种基本类型的实体 - 变量、classes 和函数 - 基于它的类型。
变量
使用以下结构声明变量:
extern int x;
这声明了一个变量 x。它没有定义它!以下一段代码将编译正常,但如果没有任何其他输入文件(例如,使用 g++ main.cpp
)尝试 link 它会由于未定义的符号而产生 link-time 错误:
extern int x;
int main() {
return x;
}
下面的一段代码定义了变量x:
int x;
如果将这一行放入文件 x.cpp,并且此文件 compiled/linked 与上面的 main.cpp 和 g++ x.cpp main.cpp -o test
一起,它将编译并且 link没有问题。您甚至可以 运行 生成可执行文件,如果您要在可执行文件 运行 之后检查退出代码,您会注意到它是 0。(因为全局变量 x 将是 default-initialized 到0).
函数
函数是通过提供它们的原型来声明的。典型的函数声明如下所示:
double foo(int x, double y);
此构造声明了一个函数 foo
,返回 double
并接受两个参数 - 一个类型为 int
,另一个类型为 double
。此声明可以出现多次。
下面的代码定义上面提到的foo
:
void foo(int x, double y) {
return x * y;
}
这个定义在整个应用中只能出现一次
函数定义与变量定义相比有一个额外的怪癖。如果将上述 foo
的定义放入 header 文件 foo.h
,这又将包含在两个 cpp 文件 1.cpp
和 2.cpp
中,它们是 compiled/linked 和 g++ 1.cpp 2.cpp -o test
一起你会得到一个 linker 错误,说 foo()
被定义了两次。这可以通过使用以下形式的 foo
声明来防止:
inline void foo(int x, double y) {
return x * y;
}
注意 inline
那里。它告诉编译器的是 foo
可以被多个 .cpp 文件包含,并且这种包含 不应该 产生 linker 错误。编译器有多种选择来实现这一点,但可以依靠它来完成它的工作。请注意,在同一个翻译单元中两次使用此定义仍然是错误的!例如,以下代码将产生编译器错误
inline void foo() { }
inline void foo() { }
值得注意的是,在class中定义的任何class方法都是隐式内联的,例如:
class A {
public:
int foo() { return 42; }
};
这里定义了A::foo()inline
.
类
类已声明y 以下构造:
class X;
上面的声明声明了classX(此时X被正式称为不完整类型),所以当不需要有关其内容的信息(例如大小或成员)时,可以使用它。例如:
X* p; // OK - no information about class X is actually required to define a pointer to it
p->y = 42; // Error - compiler has no idea if X has any member named `y`
void foo(X x); // OK - compiler does not need to generated any code for this
void foo(X x) { } // Error - compiler needs to know the size of X to generate code for foo to properly read it's argument
void bar(X* x) { } // OK - compiler needs not to know specifics of X for this
class 的定义对每个人来说都是 well-known,并遵循以下结构:
class X {
public:
int y;
};
这使得 class X 定义,现在它可以在任何上下文中使用。重要说明 - class 定义必须是每个翻译单元唯一的,但不必是每个应用程序唯一的。也就是说,每个翻译单元只能定义一次 X,但它可以在多个文件中一起使用 linked。
如何正确遵守网上解决规则
只要在生成的应用程序中多次定义同一个实体,就会发生 so-called ODR 违规。大多数时候,linker 会看到违规行为并进行投诉。但是,有些情况下 ODR 违规不会破坏 linking 反而会导致错误。例如,当定义全局变量 X 的同一个 .cpp 文件同时放入应用程序和按需加载的动态库(dlopen
)时,可能会发生这种情况。 (你真的花了几天时间试图追踪一个因此而发生的错误。)
更常见的 ODR 违规原因是:
同一实体在同一范围内的同一文件中定义两次
int x;
int x; // ODR violation
void foo() {
int x;
} // No ODR violation, foo::x is different from x in the global scope
预防:不要这样做。
同一个实体在应该声明的时候定义了两次
(in x.h)
int x;
(in 1.cpp)
#include <x.h>
void set_x(int y) {
x = y;
}
(in 2.cpp)
#include <x.h>
int get_x() {
return x;
}
虽然上面代码的智慧充其量是值得怀疑的,但它可以说明 ODR 规则。在上面的代码中,变量 x 应该在两个文件 1.cpp 和 2.cpp 之间共享,但编码不正确。相反,代码应如下所示:
(in x.h)
extern int x; //declare x
(in x.xpp)
int x; // define x
// 1.cpp and 2.cpp remain the same
预防 知道你在做什么。在需要声明实体时声明它们,不要定义它们。 如果在上面的示例中我们使用函数而不是变量,如下所示:
(in x.h)
int x_func() { return 42; }
我们会遇到一个可以通过两种方式解决的问题(如上所述)。我们可以使用 inline
函数,或者我们可以将定义移动到 cpp 文件中:
(in x.h)
int x_func();
(in x.cpp)
int x_func() { return 42; }
相同的 header 文件包含两次,导致相同的 class 定义两次 这是一个有趣的。想象一下,您有以下代码:
(in a.h)
class A { };
(in main.cpp)
#include <a.h>
#include <a.h> // compilation error!
上面的代码很少像写的那样出现,但是通过中间两次包含同一个文件是很容易的:
(in foo.h)
#include <a.h>
(in main.cpp)
#include <a.h>
#include <foo.h>
Prevention 对此的传统解决方案是使用 so-called include guards,即一个特殊的预处理器定义,它将防止double-inclusion。在这方面,a.h应该重做如下:
(in a.h)
#ifndef INCLUDED_A_H
#define INCLUDED_A_H
class A { };
#endif
上面的代码将防止 a.h 多次包含到同一个翻译单元中,因为 INCLUDED_A_H
将在第一次包含后定义,并且将在所有后续的 #ifndef
中失败一个。
一些编译器公开了其他控制包含的方法,但迄今为止,包含保护仍然是跨不同编译器统一执行此操作的方法。