類前置聲明和包含頭文件區別

一、類嵌套的疑問

C++頭文件重複包含實在是一個令人頭痛的問題,假設我們有兩個類A和B,分別定義在各自的頭文件A.h和B.h中,但是在A中要用到B,B中也要用到A,但是這樣的寫法當然是錯誤的:

classB;
classA{
     public:
        B b;
};
classB{
     public:
        A a;
};

因爲在A對象中要開闢一塊屬於B的空間,而B中又有A的空間,是一個邏輯錯誤,無法實現的,在這裏我們只需要把其中的一個A類中的B類型成員改成指針形式就可以避免這個無限延伸的怪圈了,爲什麼要更改A而不是B?因爲就算你在B中做了類似的動作,也仍然會編譯錯誤,表面上這僅僅上一個先後順序的問題

爲什麼會這樣呢?因爲C++編譯器自上而下編譯源文件的時候,對每一個數據的定義,總是需要知道定義的數據類型的大小在預先聲明語句classB;之後,編譯器已經知道B是一個類,但是其中的數據卻是未知的,因此B類型的大小也不知道這樣就造成了編譯失敗,VC++6.0下會得到如下編譯錯誤:

error C2079: 'b' uses undefined class 'B'

將A中的b更改爲B指針類型之後,由於在特定的平臺上,指針所佔的空間是一定的(在Win32平臺上是4字節),這樣可以通過編譯

二、不同頭文件中的類的嵌套

在實際編程中,不同的類一般是放在不同的相互獨立的頭文件中的,這樣兩個類在相互引用時又會有不一樣的問題,重複編譯是問題出現的根本原因爲了保證頭文件僅被編譯一次,在C++中常用的辦法是使用條件編譯命令在頭文件中我們常常會看到以下語句段(以VC++6.0自動生成的頭文件爲例):

#IFNDEF  TESTSTR
#define TESTSTR
     //很多語句
#endif

意思是如果沒有定義過這個宏,那麼就定義它,然後執行直到#endif的所有語句如果下次在與要這段代碼,由於已經定義了那個宏,因此重複的代碼不會被再次執行這實在是一個巧妙而高效的辦法在高版本的VC++上,還可以使用這個命令來代替以上的所有:

#pragma once

它的意思是,本文件內的代碼只被使用一次

但是不要以爲使用了這種機制就全部搞定了,比如在以下的代碼中:

//文件A.h中的代碼

#pragmaonce
#include"B.h"
classA{
     public:
         B*b;
};

//文件B.h中的代碼

#pragmaonce
#include"A.h"
classB{
     public:
         A*a;
};

這裏兩者都使用了指針成員,因此嵌套本身不會有什麼問題,在主函數前面使用#include "A.h"之後,主要編譯錯誤如下:

error C2501: 'A' : missing storage-class or typespecifiers

仍然是類型不能找到的錯誤其實這裏仍然需要前置聲明分別添加前置聲明之後,可以成功編譯了代碼形式如下:

//文件A.h中的代碼

#pragmaonce
#include"B.h"
class B;
classA{
     public:
         B*b;
};

//文件B.h中的代碼

#pragmaonce
#include"A.h"
class A;
classB{
     public:
         A*a;
};

這樣至少可以說明,頭文件包含代替不了前置聲明,有的時候只能依靠前置聲明來解決問題,我們還要思考一下,有了前置聲明的時候頭文件包含還是必要的嗎?我們嘗試去掉A.h和B.h中的#include行,發現沒有出現新的錯誤那麼究竟什麼時候需要前置聲明,什麼時候需要頭文件包含呢?

 

三、兩點原則

頭文件包含其實是一想很煩瑣的工作,不但我們看着累,編譯器編譯的時候也很累,再加上頭文件中常常出現的宏定義感覺各種宏定義的展開是非常耗時間的,遠不如自定義函數來得速度我僅就不同頭文件源文件間的句則結構問題提出兩點原則,僅供參考:

第一個原則:如果可以不包含頭文件,那就不要包含,這時候前置聲明可以解決問題,如果使用的僅僅是一個類的指針,沒有使用這個類的具體對象(非指針),也沒有訪問到類的具體成員,那麼前置聲明就可以了,因爲指針這一數據類型的大小是特定的,編譯器可以獲知.

 

第二個原則:儘量在CPP文件中包含頭文件,而非在頭文件中假設類A的一個成員是是一個指向類B的指針,在類A的頭文件中使用了類B的前置聲明並便宜成功,那麼在A的實現中我們需要訪問B的具體成員,因此需要包含頭文件,那麼我們應該在類A的實現部分(CPP文件)包含類B的頭文件而非聲明部分(H文件).
注:此文章屬於轉載

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章