1. 程式人生 > >C/C++——標頭檔案重複包含問題

C/C++——標頭檔案重複包含問題

C++程式設計中,一般將類的宣告,類的定義分開,將類的宣告放在.h的標頭檔案中,而將類的定義放在.cpp的原始檔中,這樣我們的程式設計起來更加的模組化也清晰明瞭。但是,這樣的設計也會帶來一些問題,一個典型的問題就是重複定義問題。如果從一個類派生出一個類,派生類宣告時需要包含基類的標頭檔案,如果再在主函式包含這個標頭檔案,編譯時就報錯,編譯器出現重複定義的問題,給出重定義錯誤提示。

我們先看看下面這個程式。首先我們在新建工程下新建兩個.h的標頭檔案(檔名分別為people.h和 student.h),繼續新建三個.cpp的原始檔(檔名分別是people.cpp、student.cpp、main.cpp),然後編輯相應的程式碼。

//people.h標頭檔案

#include<iostream>

using namespace std;

class people            //定義一個類people,類有一個成員函式breath()

{

public:

       voidbreath();

};

//student.h標頭檔案        //定義一個類student,從類people繼承得到,重寫成員函式breath()

#include"people.h"

#include<iostream>

using namespace std;

class student :public people     

{

public:

       voidbreath();

};

//people.cpp原始檔    //定義類people的breath()方法

#include"people.h"

void people::breath()

{    

cout << "poeple"<< endl;

}

//student.cpp原始檔   //重寫類student的breath()方法

#include"student.h"

void student::breath()

{    

cout << "student"<< endl;

 }

//main.cpp原始檔

#include"people.h"

#include"student.h"

int main()

{    

       people p;        //例項化類people的一個物件p

       student s;       //例項化類student的一個物件s

       p.breath();      //呼叫函式

       s.breath();

       return 0;

}

在vs2013中對三個原始檔進行分別編譯,對於people.cpp和student.cpp編譯時,沒有錯誤,而對main.cpp編譯時顯示錯誤errorC2011: “people”:“class”型別重定義,說明類重複定義。我們來分析一下原因。在main.cpp原始檔中,發現#include"people.h"此句程式碼,編譯器回去查詢people.h標頭檔案,發現people這個類的定義,繼續執行,執行到#include"student.h"這句程式碼時,編譯器便會去查詢student.h標頭檔案,在student.h標頭檔案中,編譯器執行到#include" people.h"時,便又去查詢people.h標頭檔案中的程式碼,再次發現類people的定義,這樣,由於include巢狀造成類people重複定義了兩次,編譯器便會報錯。

對於這種輕量程式,我們可以通過將所有程式碼寫在一個原始檔中,包括類的宣告和定義,還有主函式,這樣就不用寫#include"people.h"#include"student.h"但對於大程式,這種方式顯然是行不通的,不但會讓程式碼顯得冗長而且沒有條理。因此對於這種情況,最常用的方法是用條件編譯#ifndef…#define…#endif語句。修改上面people.h和student.h檔案。

//people.h標頭檔案

#ifndef A

#define A

#include<iostream>

using namespace std;

class people

{

public:

       voidbreath();

};

#endif

//student.h標頭檔案   

#ifndef B

#define B

#include"people.h"

#include<iostream>

using namespace std;

class student :public people

{

public:

       voidbreath();

};

#endif

經過上面的修改,這個程式就可以通過編譯了。觀察改寫後的程式碼,發現我們在類的定義前後分別加上了#ifndef…#define…#endif語句,當編譯器編譯main.cpp原始檔中的程式碼,發現#include"people.h"此句程式碼,編譯器回去查詢people.h標頭檔案,走到#ifndef B時,編譯器會做出如下的判斷,若A沒有被定義,便定義它(#define A被執行)繼續往下走,走到#include"student.h"這句程式碼時,編譯器便會去查詢student.h標頭檔案,在student.h標頭檔案中,編譯器走到#include " people.h"時,便又去查詢people.h標頭檔案中的程式碼,與上面的一樣,編譯器會判斷A定義了沒有,若沒有,便進行定義,反之,將跳過#ifndef…#endif間的程式碼,繼續往下走,直到程式編譯完畢。所以通過條件編譯就可以解決重複定義的問題。#ifndef B語句中的B表示一個標示符,也可以用自己想用的不與程式衝突的其他標示符,通常用大寫,只要每個標頭檔案的標示符是唯一的。一般我們可以取一些表達明瞭的標示符,如在這裡可以用 PEOPLE_H。

雖然不是所有的標頭檔案中都要用條件編譯,但是,在標頭檔案中使用條件編譯避免重複包含,只有好處沒有壞處。有時候雖然重複包含一個頭檔案不會報錯,但同樣的程式碼多次編譯也是浪費系統資源,所以儘可能地要避免這種重複編譯情況。

參考文獻: