這里說的重復定義其實包含兩個意思:
1,頭文件重復包含
2,變量或函數重復定義
對問題1,比如這樣:
有一個頭文件 c.h 。
在頭文件a.h中
#include "c.h"
在頭文件b.h中
#include "a.h"
#include "c.h"
這樣就會導致重復包含,解決辦法如下:
在c.h中
#ifndef C_H #define C_H //c.h中所有內容 #endinf
這樣,在第二次引用"c.h"時,由於已經定義了C_H,將不會再次引用"c.h"
當然,也要避免在頭文件里include,如果include是寫在cpp里面的話,就不會出現相互包含導致編譯出錯的問題,但是!!!!!如果頭文件里的函數寫了實現,重復包含的話仍然是要出錯的,具體看問題2的解釋
對於問題2:
如果在c.h中,有一個函數實現,比如:
int integer_add(const int a, const int b) { return a+b; }
在a.cpp和b.cpp中都包含的c.h,那么編譯的時候:
a.cpp編譯得到a.obj,b.cpp編譯得到b.obj;
在鏈接時期,a.obj和b.obj和運行庫連接起來生成可執行文件的時候,就會沖突,因為兩個obj里都有一份integer_add的函數實現,導致鏈接器不知道對於調用者,應該使用哪一個副本
解決辦法:
1.使用inline修飾函數
使用inline,意味着編譯器會在調用此函數的地方把函數的目標代碼直接插入,而不是放置一個真正的函數調用,也就是說這個函數實際上已經不再存在,而是像宏一樣被就地展開了。
除了體積變大,使用inline還有這樣的問題:inline嚴格來算不是關鍵字,inline是對編譯器進行請求而不是強制,所以在一些特殊情況下,編譯器並不會真正使用inline進行修飾
類函數在類定義的時候就實現的情況下,缺省就是inline的
模板函數也是相似的
2.使用static修飾函數
使用static意味着,所有包含此頭文件的源文件中都會存在此函數的一份副本,代碼也有一定膨脹,但是相互不沖突,因為static關鍵字保證了該函數可見度為單個源文件
通過static修飾類函數,就可以使用這樣的調用方式:Integer::add(i,j);
對於virtual函數,它總是生成一份代碼,即使你顯式使用inline關鍵字修飾,因為:virtual函數地址會被寫到類的v-table里,在運行期才被調用。
對於類被多個cpp包含的情況下,沒有出現沖突,原因是遵守“單一定義規則”(One_Definition Rule, ODR) :如果對同一個類的兩個定義完全相同且出現在不同編譯單元,會被當作同一個定義
相關引用: