2017-02-18 103 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」。