標頭檔案裡面實現一個函式,在兩個cpp中包含,則重複定義,如果加上inline則不會報錯
阿新 • • 發佈:2019-02-01
比如myclass.h定義了一個類myclass(只定義類,不定義成員函式), file1.cpp裡#include "myclass.h",編譯得到file1.obj; file2.cpp裡也#include "myclass.h",編譯得到file2.obj; 那麼把file1.obj和file2.obj(和執行庫)連線起來生成可執行檔案的時候,為什麼不會有衝突呢?2個檔案都包含了myclass類的定義。
因為這遵守“單一定義規則”(One-Definition Rule, ODR)。根據此規則, 如果對同一個類的兩個定義完全相同且出現在不同編譯單位,會被當作同一個定義。 這裡標頭檔案分別被兩個不同的編譯單位(file1.cpp, file2.cpp)包含,滿足ODR規則,會被當作同一個定義。 所以不會有衝突。 此外,模板和inline函式也適用此規則。
以g++為例,類的程式碼出現在包含類方法定義的編譯單元,引用他的編譯單元會在連結時尋找類程式碼。如果.h中在類定義大括號內直接定義了函式,那麼類程式碼會出現在每一個include這個.h的編譯單元中。 以前者為例:
myclass.h: class MyClass { public: MyClass(int i); void add(); int m; }; myclass.cpp: #include "myclass.h" MyClass::MyClass(int i) { this->m = i; add(); } void MyClass::add() { this->m ++; } file1.cpp: #include "myclass.h" void f1() { MyClass mc(10); } file2.cpp: #include "myclass.h" void f2() { MyClass mc(20); } main.cpp: int main() { void f1(); void f2(); f1(); f2(); } file1.o反彙編節選: 0: 55 push %ebp 1: 89 e5 mov %esp,%ebp 3: 83 ec 28 sub $0x28,%esp 6: c7 44 24 04 0a 00 00 movl $0xa,0x4(%esp) d: 00 e: 8d 45 f4 lea -0xc(%ebp),%eax 11: 89 04 24 mov %eax,(%esp) 14: e8 fc ff ff ff call 15 <_Z2f1v+0x15> 15: R_386_PC32 _ZN7MyClassC1Ei myclass.o反彙編節選: 0000001c <_ZN7MyClassC1Ei>: 1c: 55 push %ebp 1d: 89 e5 mov %esp,%ebp 1f: 83 ec 18 sub $0x18,%esp 22: 8b 45 08 mov 0x8(%ebp),%eax 25: 8b 55 0c mov 0xc(%ebp),%edx 28: 89 10 mov %edx,(%eax) 2a: 8b 45 08 mov 0x8(%ebp),%eax 2d: 89 04 24 mov %eax,(%esp) 30: e8 fc ff ff ff call 31 <_ZN7MyClassC1Ei+0x15> 31: R_386_PC32 _ZN7MyClass3addEv 可見類程式碼在myclass.o, file1.o在類定義的幫助下,通過_ZN7MyClassC1Ei引用類方法程式碼。 其它細節可以反彙編程式碼、看elf/pe等檔案格式文件以及編譯器原始碼。