1. 程式人生 > >為什麼類的定義應當寫在標頭檔案中,從而被多個原始檔包含?

為什麼類的定義應當寫在標頭檔案中,從而被多個原始檔包含?

比如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函式也適用此規則。
追問
類的定義在目標檔案比如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檔案不參與編譯,標頭檔案的作用就是告訴編譯器,
有這個類,但是類的實現在其他位置,編譯時,編譯器不會去找類的實現,
連結時編譯器才會去尋找這個類的實現。