头文件包含函数体,会导致重复定义吗?

时间:2017-02-18 01:58:25

标签: c++ function duplicates inline definition

我做了一个简单的实验,一个带有类定义和函数定义的“.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作为名称重整)

然后我将testInline.h-> g()定义修改为:

inline void g(){}

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

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

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

2 个答案:

答案 0 :(得分:3)

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

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

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

答案 1 :(得分:3)

  

我用过“#pragma once”,

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

#pragma once并不意味着“只在正在编译的一个翻译单元中包含此头文件”。这意味着“每个翻译单元包括一个头文件,即使翻译单元直接或间接地包括头文件两次或更多次”。因此,每个翻译单元包括头文件,并从头文件本身定义功能/方法。由于最终由两个翻译单元定义了相同的功能或方法,因此在链接时最终会出现重复。

  

在C ++中,“内联”关键字基本上没用,因为   编译器将决定是否内联函数?

确实,编译器决定该函数是否实际内联。但是,inline关键字指定是否处理函数定义,就好像它在每次使用它时都是逻辑内联的,而不是实际定义的。因此,使用inline关键字不会导致重复定义,因为从逻辑上讲,函数会在其每个引用处插入内联。

确实,这是否真的发生,或者编译器是否产生非内联代码,都取决于编译器。但是,C ++要求函数被编译为“好像”它被内联;因此,即使编译器决定不内联函数,它也必须采取必要的步骤来确保函数的重复非内联副本不会导致格式错误的程序。

  

而且,为什么带有.h文件中代码的C :: f()不会报告重复,

因为类方法内部定义的类方法实际上是内联定义,即使没有明确指定inline关键字。