多个定义错误,包括带有来自多个源的内联代码的 C++ 头文件
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/212006/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
multiple definition error including c++ header file with inline code from multiple sources
提问by Paolo Tedesco
I have a c++ header file containing a class. I want to use this class in several projects, bu I don't want to create a separate library for it, so I'm putting both methods declarations and definitions in the header file:
我有一个包含类的 C++ 头文件。我想在几个项目中使用这个类,但我不想为它创建一个单独的库,所以我将方法声明和定义都放在头文件中:
// example.h
#ifndef EXAMPLE_H_
#define EXAMPLE_H_
namespace test_ns{
class TestClass{
public:
void testMethod();
};
void TestClass::testMethod(){
// some code here...
}
} // end namespace test_ns
#endif
If inside the same project I include this header from more than one cpp file, I get an error saying "multiple definition of test_ns::TestClass::testMethod()
", while if I put the method definition inside the class body this does not happen:
如果在同一个项目中,我从多个 cpp 文件中包含此头文件,则会收到一条错误消息,提示“ multiple definition of test_ns::TestClass::testMethod()
”,而如果我将方法定义放在类主体中,则不会发生这种情况:
// example.h
#ifndef EXAMPLE_H_
#define EXAMPLE_H_
namespace test_ns{
class TestClass{
public:
void testMethod(){
// some code here...
}
};
} // end namespace test_ns
#endif
Since the class is defined inside a namespace, shouldn't the two forms be equivalent? Why is the method considered to be defined twice in the first case?
既然类是在命名空间中定义的,那么这两种形式不应该是等价的吗?为什么在第一种情况下认为方法被定义了两次?
采纳答案by workmad3
These are not equivalent. The second example given has an implicit 'inline' modifier on the method and so the compiler will reconcile multiple definitions itself (most likely with internal linkage of the method if it isn't inlineable).
这些不是等价的。给出的第二个示例在方法上有一个隐式的“内联”修饰符,因此编译器将协调多个定义本身(如果它不可内联,则很可能与方法的内部链接)。
The first example isn't inline and so if this header is included in multiple translation units then you will have multiple definitions and linker errors.
第一个示例不是内联的,因此如果此标头包含在多个翻译单元中,那么您将有多个定义和链接器错误。
Also, headers should really always be guarded to prevent multiple definition errors in the same translation unit. That should convert your header to:
此外,标题应该始终受到保护,以防止同一翻译单元中出现多个定义错误。这应该将您的标题转换为:
#ifndef EXAMPLE_H
#define EXAMPLE_H
//define your class here
#endif
回答by QBziZ
Inside the class body is considered to be inline by the compiler. If you implement outside of body, but still in header, you have to mark the method as 'inline' explicitly.
类体内部被编译器认为是内联的。如果您在正文之外实现,但仍在标题中,则必须将该方法显式标记为“内联”。
namespace test_ns{
class TestClass{
public:
inline void testMethod();
};
void TestClass::testMethod(){
// some code here...
}
} // end namespace test_ns
Edit
编辑
For myself it often helps to solve these kinds of compile problems by realizing that the compiler does not see anything like a header file. Header files are preprocessed and the compiler just sees one huge file containing every line from every (recursively) included file. Normally the starting point for these recursive includes is a cpp source file that is being compiled. In our company, even a modest looking cpp file can be presented to the compiler as a 300000 line monster.
对我自己来说,通过意识到编译器看不到头文件之类的东西,通常有助于解决这些类型的编译问题。头文件经过预处理,编译器只看到一个包含每个(递归)包含文件中每一行的巨大文件。通常,这些递归包含的起点是正在编译的 cpp 源文件。在我们公司,即使是看起来不起眼的 cpp 文件也可以作为 300000 行的怪物呈现给编译器。
So when a method, that is not declared inline, is implemented in a header file, the compiler could end up seeing void TestClass::testMethod() {...} dozens of times in the preprocessed file. Now you can see that this does not make sense, same effect as you'd get when copy/pasting it multiple times in one source file. And even if you succeeded by only having it once in every compilation unit, by some form of conditional compilation ( e.g. using inclusion brackets ) the linker would still find this method's symbol to be in multiple compiled units ( object files ).
因此,当一个未声明为内联的方法在头文件中实现时,编译器最终可能会在预处理文件中看到 void TestClass::testMethod() {...} 数十次。现在您可以看到这是没有意义的,与在一个源文件中多次复制/粘贴它时获得的效果相同。即使您在每个编译单元中只使用一次它就成功了,通过某种形式的条件编译(例如使用包含括号),链接器仍然会发现此方法的符号位于多个编译单元(目标文件)中。
回答by PW.
Don't put a function/method definition in an header file unless they are inlined (by defining them directly in a class declaration or explicity specified by the inline keyword)
不要将函数/方法定义放在头文件中,除非它们是内联的(通过在类声明中直接定义它们或由 inline 关键字明确指定)
header files are (mostly) for declaration (whatever you need to declare). Definitions allowed are the ones for constants and inlined functions/methods (and templates too).
头文件(大部分)用于声明(无论您需要声明什么)。允许的定义是常量和内联函数/方法(以及模板)的定义。
回答by sastanin
Actually it is possible to have definitions in a single header file (without a separate .c/.cpp file) and still be able to use it from multiple source files.
实际上,可以在单个头文件中定义定义(没有单独的 .c/.cpp 文件),并且仍然可以从多个源文件中使用它。
Consider this foobar.h
header:
考虑这个foobar.h
标题:
#ifndef FOOBAR_H
#define FOOBAR_H
/* write declarations normally */
void foo();
void bar();
/* use conditional compilation to disable definitions when necessary */
#ifndef ONLY_DECLARATIONS
void foo() {
/* your code goes here */
}
void bar() {
/* your code goes here */
}
#endif /* ONLY_DECLARATIONS */
#endif /* FOOBAR_H */
If you use this header in only one source file, include and use it normally.
Like in main.c
:
如果仅在一个源文件中使用此标头,请正常包含并使用它。就像在main.c
:
#include "foobar.h"
int main(int argc, char *argv[]) {
foo();
}
If there're other source files in your project which require foobar.h
, then #define ONLY_DECLARATIONS
macro before including it.
In use_bar.c
you may write:
如果您的项目中还有其他需要的源文件foobar.h
,则#define ONLY_DECLARATIONS
在包含它之前使用宏。在use_bar.c
你可以写:
#define ONLY_DECLARATIONS
#include "foobar.h"
void use_bar() {
bar();
}
After compilation use_bar.o and main.o can be linked together without errors, because only one of them (main.o) will have implementation of foo() and bar().
编译后 use_bar.o 和 main.o 可以链接在一起而不会出错,因为只有其中之一(main.o)会实现 foo() 和 bar()。
That's slightly non-idiomatic, but it allows to keep definitions and declarations together in one file. I feel like it's a poor man's substitute for true modules.
这有点不习惯,但它允许将定义和声明放在一个文件中。我觉得这是一个穷人的真正模块的替代品。
回答by sastanin
Your first code snippet is falling foul of C++'s "One Definition Rule" - see here for a link to a Wikipedia article describing ODR.You're actually falling foul of point #2 because every time the compiler includes the header file into a source file, you run into the risk of the compiler generating a globally visible definition of test_ns::TestClass::testMethod()
. And of course by the time you get to link the code, the linker will have kittens because it will find the same symbol in multiple object files.
您的第一个代码片段违反了 C++ 的“一个定义规则” - 请参阅此处获取描述 ODR 的维基百科文章的链接。您实际上违反了第 2 点,因为每次编译器将头文件包含到源文件中时,您都会遇到编译器生成全局可见的test_ns::TestClass::testMethod()
. 当然,当您开始链接代码时,链接器将拥有小猫,因为它会在多个目标文件中找到相同的符号。
The second snippet works because you've inlined the definition of the function, which means that even if the compiler doesn't generate any inline code for the function (say, you've got inlining turned off or the compiler decides the function is too big to inline), the code generated for the function definition will be visible in the translation unit only, as if you'd stuck it in an anonymous namespace. Hence you get multiple copies of the function in the generated object code that the linker may or may not optimize away depending on how smart it is.
第二个片段有效,因为您已经内联了函数的定义,这意味着即使编译器没有为该函数生成任何内联代码(例如,您已经关闭了内联,或者编译器认为该函数太big to inline),为函数定义生成的代码将仅在翻译单元中可见,就好像您将它放在匿名命名空间中一样。因此,您会在生成的目标代码中获得该函数的多个副本,链接器可能会或可能不会优化掉,具体取决于它的智能程度。
You could achieve a similar effect in your first code snippet by prefixing TestClass::testMethod()
with inline
.
您可以通过前缀实现你的第一个代码片段类似的效果TestClass::testMethod()
用inline
。
回答by Mahendra
//Baseclass.h or .cpp
#ifndef CDerivedclass
#include "Derivedclass.h"
#endif
or
//COthercls.h or .cpp
#ifndef CCommonheadercls
#include "Commonheadercls.h"
#endif
I think this suffice all instances.