头文件包含函数体,会导致重复定义吗?
Header file contains function body, will lead to duplicated definition?
我做了一个简单的实验,一个“.h”文件,有一个class定义和一个函数定义,如下:
$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(){}
好吧,编译成功了。 "inline" 关键字在 C++ 中不是基本上没用吗,因为编译器会决定它是否会内联一个函数?
而且,为什么 C::f() with code in .h file does not report duplication, while a C-style function g() does?为什么 class C 没有 "have to" 为其 "f()" 函数添加 "inline",而 g() 必须使用 "inline"?
希望我已经清楚地说明了我的问题。感谢您的帮助。
inline
关键字不是没用。只是不一定控制函数是否真正内联。
inline
关键字将函数标记为可能在多个翻译单元中定义。 (定义和含义必须全部相同。)您应该将头文件中定义的函数标记为 inline
.
在 class 定义中定义的函数,例如您的 C::f
,会自动被视为“inline
”。
I've used "#pragma once",
是的,你做到了。两个翻译单元中的每一个都只有效地处理了一次头文件。即使没有 pragma,每个翻译单元都会这样做,因为每个翻译单元只包含一次头文件。
#pragma once
并不代表 "include this header file in just one of the translation units being compiled"。意思是"include this header file once per translation unit, even if the translation unit directly, or indirectly, includes the header file two or more times"。因此,每个翻译单元都包含头文件,并从头文件本身定义 functions/methods 。由于相同的函数或方法最终由两个翻译单元定义,因此您在 link 时得到了一个副本。
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
关键字不会导致重复定义,因为从逻辑上讲,该函数是在其每个引用处内联插入的。
确实,这是否实际发生,或者编译器是否生成 non-inlined 代码,取决于编译器。但是,C++ 要求函数被编译 "as if" 它是内联的;因此,即使编译器决定不内联函数,它也必须采取任何必要的步骤来确保函数的重复 non-inlined 副本不会导致 ill-formed 程序。
And,why C::f() with code in .h file doesn't report duplication,
因为 class 方法在内部定义 class 的定义实际上是一个内联定义,即使未明确指定 inline
关键字也是如此。
我做了一个简单的实验,一个“.h”文件,有一个class定义和一个函数定义,如下:
$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(){}
好吧,编译成功了。 "inline" 关键字在 C++ 中不是基本上没用吗,因为编译器会决定它是否会内联一个函数?
而且,为什么 C::f() with code in .h file does not report duplication, while a C-style function g() does?为什么 class C 没有 "have to" 为其 "f()" 函数添加 "inline",而 g() 必须使用 "inline"?
希望我已经清楚地说明了我的问题。感谢您的帮助。
inline
关键字不是没用。只是不一定控制函数是否真正内联。
inline
关键字将函数标记为可能在多个翻译单元中定义。 (定义和含义必须全部相同。)您应该将头文件中定义的函数标记为 inline
.
在 class 定义中定义的函数,例如您的 C::f
,会自动被视为“inline
”。
I've used "#pragma once",
是的,你做到了。两个翻译单元中的每一个都只有效地处理了一次头文件。即使没有 pragma,每个翻译单元都会这样做,因为每个翻译单元只包含一次头文件。
#pragma once
并不代表 "include this header file in just one of the translation units being compiled"。意思是"include this header file once per translation unit, even if the translation unit directly, or indirectly, includes the header file two or more times"。因此,每个翻译单元都包含头文件,并从头文件本身定义 functions/methods 。由于相同的函数或方法最终由两个翻译单元定义,因此您在 link 时得到了一个副本。
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
关键字不会导致重复定义,因为从逻辑上讲,该函数是在其每个引用处内联插入的。
确实,这是否实际发生,或者编译器是否生成 non-inlined 代码,取决于编译器。但是,C++ 要求函数被编译 "as if" 它是内联的;因此,即使编译器决定不内联函数,它也必须采取任何必要的步骤来确保函数的重复 non-inlined 副本不会导致 ill-formed 程序。
And,why C::f() with code in .h file doesn't report duplication,
因为 class 方法在内部定义 class 的定义实际上是一个内联定义,即使未明确指定 inline
关键字也是如此。