為什麼類的定義應當寫在標頭檔案中,從而被多個原始檔包含?
阿新 • • 發佈:2019-01-28
比如myclass.h定義了一個類myclass(只定義類,不定義成員函式),
file1.cpp裡#include "myclass.h",編譯得到file1.obj;
file2.cpp裡也#include "myclass.h",編譯得到file2.obj;
那麼把file1.obj和file2.obj(和執行庫)連線起來生成可執行檔案的時候,為什麼不會有衝突呢?2個檔案都包含了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函式也適用此規則。
類的定義在目標檔案比如file1.obj裡是怎麼體現的呢?它產生目的碼嗎?類的定義不是變數名,也不是函式名,那麼在程式碼段、資料段或符號表裡面應該都沒有條目? 還是說,類的定義只是讓編譯器識別下文的這個類名?而對目標檔案沒有任何體現?回答
細節依賴於編譯器的實現。 以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(); }
可見類程式碼在myclass.o, file1.o在類定義的幫助下,通過_ZN7MyClassC1Ei引用類方法程式碼。 其它細節可以反彙編程式碼、看elf/pe等檔案格式文件以及編譯器原始碼。 所謂的編譯只編譯cpp檔案,.h檔案不參與編譯,標頭檔案的作用就是告訴編譯器, 有這個類,但是類的實現在其他位置,編譯時,編譯器不會去找類的實現, 連結時編譯器才會去尋找這個類的實現。