2017-02-18 100 views
1

我做了一个简单的实验,一个“.h”头文件使用一个类定义和功能可按定义如下:头文件包含函数体,会导致重复的定义?

$cat testInline.h 
#pragma once 
class C{ 
public: 
    void f(){} 
}; 
void g(){} 

然后2对用户来说,这.h文件中的:

$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”,我仍然无法从报告克(重复的定义停止编译)(__ Z1gv如名称重整)

然后我改性testInline.h->克()的定义是这样的:

inline void g(){} 

好了,它编译。是不是在C++中,“内联”关键字基本上是无用的,因为编译器会决定它是否将内联函数?

而且,为什么具有.h文件中的代码的C :: f()不会报告重复,而C风格的函数g()呢?为什么C类不必为其“f()”函数添加“内联”,而g()必须使用“内联”?

希望我已经说清楚了我的问题。谢谢你的帮助。

回答

3

I've used "#pragma once",

是的,你做到了。并且两个翻译单元中的每一个都有效地处理了头文件一次。即使没有编译指示,每个编译器也会这样做,因为每个编译单元只包含一次头文件。

#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关键字。

+0

“C++要求函数被编译”就好像“它被内联;”,呃,考虑一个递归函数。 ;-) –

3

inline关键字不是没用的。只是它不一定控制函数是否实际内联。

inline关键字标记可能在多个翻译单元中定义的函数。 (所有这些定义和含义必须相同。)您应该将标头文件中定义的函数标记为inline

定义在类定义中的函数(如C::f)自动被视为“inline”。