1. 程式人生 > >標頭檔案裡面實現一個函式,在兩個cpp中包含,則重複定義,如果加上inline則不會報錯

標頭檔案裡面實現一個函式,在兩個cpp中包含,則重複定義,如果加上inline則不會報錯

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

比如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等檔案格式文件以及編譯器原始碼。