我做了一个简单的实验,一个带有类定义和函数定义的“.h”文件,如下:
$cat testInline.h
#pragma once
class C{
public:
void f(){}
};
void g(){}
然后这个 .h 文件的 2 个用户:
$cat use01.cpp
#include"testInline.h"
void g01(){
g();
C obj1;
obj1.f();
}
$cat use02.cpp
#include"testInline.h"
int main(){
g();
C obj2;
obj2.f();
return 0;
}
我将它们编译在一起并得到一个错误:
$g++ use01.cpp use02.cpp
duplicate symbol __Z1gv in:
/var/folders/zv/b953j0_55vldj97t0wz4qmkh0000gn/T/use01-34f300.o
/var/folders/zv/b953j0_55vldj97t0wz4qmkh0000gn/T/use02-838e05.o
ld: 1 duplicate symbol for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
看起来真的很奇怪:我已经使用了“#pragma once”,仍然无法阻止编译器报告 g()(__Z1gv as name mangling) 的重复定义
然后我将 testInline.h->g() 定义修改为:
inline void g(){}
好吧,它编译。难道在 C++ 中,“inline”关键字基本上没有用,因为编译器将决定它是否内联一个函数?
而且,为什么 C::f() 代码在 .h 文件中不报告重复,而 C 风格的函数 g() 报告重复?为什么 C 类“不必”为其“f()”函数添加“内联”,而 g() 必须使用“内联”?
希望我已经清楚地说明了我的问题。感谢您的帮助。
最佳答案
I've used "#pragma once",
是的,你做到了。两个翻译单元中的每一个都只有效地处理了一次头文件。即使没有 pragma,每个翻译单元都会这样做,因为每个翻译单元只包含一次头文件。
#pragma once
并不意味着“仅将此头文件包含在一个正在编译的翻译单元中”。它的意思是“每个翻译单元包含一次这个头文件,即使翻译单元直接或间接地包含头文件两次或更多次”。因此,每个翻译单元都包含头文件,并从头文件本身定义函数/方法。由于相同的函数或方法最终由两个翻译单元定义,因此您最终在链接时得到了一个拷贝。
Isn't it in C++ that "inline" keyword is basically useless, because compilers will decide whether it'll inline a function or not?
确实,编译器决定了函数是否真正被内联。但是,inline
关键字指定是否处理函数定义,就好像它在每次使用时都被逻辑内联,而不是实际定义。因此,使用 inline
关键字不会导致重复定义,因为从逻辑上讲,该函数是在其每个引用处内联插入的。
的确,这是否真的发生,或者编译器是否产生非内联代码,都取决于编译器。但是,C++ 要求函数“如同”被内联一样进行编译;因此,即使编译器决定不内联该函数,它也必须采取任何必要的步骤来确保该函数的重复非内联拷贝不会导致格式错误的程序。
And,why C::f() with code in .h file doesn't report duplication,
因为类方法在类的定义内部定义实际上是一个内联定义,即使未明确指定 inline
关键字也是如此。
关于c++ - 头文件包含函数体,会导致重复定义吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42310227/