有一定c++開發經驗的朋友可能會遇到這樣的場景:兩個類a與b是強耦合關係,類a要引用b的物件,類b也要引用類a的物件。好的,不難,我的第一直覺讓我寫出這樣的**:
// a.h
#include "b.h"
class a
;
#include "a.h"
a::a(void)
a::~a(void)
// b.h
#include "a.h"
class b
;
// b.cpp
#include "b.h"
b::b(void)
b::~b(void)
好的,完成,編譯一下a.cpp,不通過。再編譯b.cpp,還是不通過。編譯器都被搞暈了,編譯器去編譯a.h,發現包含了b.h,就去編譯b.h。編譯b.h的時候發現包含了a.h,但是a.h已經編譯過了(其實沒有編譯完成,可能編譯器做了記錄,a.h已經被編譯了,這樣可以避免陷入死迴圈。編譯出錯總比死迴圈強點),就沒有再次編譯a.h就繼續編譯。後面發現用到了a的定義,這下好了,a的定義並沒有編譯完成,所以找不到a的定義,就編譯出錯了。提示資訊如下:
1>d:/vs2010/test/test/a.h(5): error c2146: syntax error : missing ';' before identifier 'b'
1>d:/vs2010/test/test/a.h(5): error c4430: missing type specifier - int assumed. note: c++ does not support default-int
1>d:/vs2010/test/test/a.h(5): error c4430: missing type specifier - int assumed. note: c++ does not support default-int
那怎麼辦?有辦法,c++為我們提供了前置宣告。前置宣告是什麼?舉個形象點的例子,就是我要蓋乙個屋子(chouse),光有屋子還不行啊,我還得有床(cbed)。但是屋子還沒蓋好,總不能先買床吧,床的大小我定了,改天買。先得把房子蓋好,蓋房子的時候我先給床留個位置,等房子蓋好了,我再決定買什麼樣的床。前置宣告就是我在宣告乙個類(chouse)的時候,用到了另外乙個類的定義(cbed),但是cbed還沒有定義呢,而且我還先不需要cbed的定義,只要知道cbed是乙個類就夠了。那好,我就先宣告類cbed,告訴編譯器cbed是乙個類(不用包含cbed的標頭檔案):
class cbed;
然後在chouse中用到cbed的,都用cbed的指標型別代(因為指標型別固定大小的,但是cbed的大小只用知道了cbed定義才能確定)。等到要實現chouse定義的時候,就必須要知道cbed的定義了,那是再包好cbed的標頭檔案就行了。
前置宣告有時候很有用,比如說兩個類相互依賴的時候要。還有前置宣告可以減少標頭檔案的包含層次,減少出錯可能。上面說的例子。
// house.h
class cbed; // 蓋房子時:現在先不買,肯定要買床的
class chouse
;
// house.cpp
#include "bed.h"
#include "house.h" // 等房子開始裝修了,要買床了
chouse::chouse(void)
chouse::~chouse(void)
void chouse::gotobed()
// bed.h
class cbed
;
// bed.cpp
#include "bed.h"
cbed::cbed(void)
cbed::~cbed(void)
void cbed::sleep()
注意這裡有陷阱:
1、cbed* bed;必須用指標或引用
引用版本:
// house.h
class cbed; // 蓋房子時:現在先不買,肯定要買床的
class chouse
;
// house.cpp
#include "bed.h"
#include "house.h" // 等房子開始裝修了,要買床了
chouse::chouse(void)
: bed(*new cbed())
chouse::chouse(cbed& bedtmp)
: bed(bedtmp)
chouse::~chouse(void)
void chouse::gotobed()
2、不能在chouse的宣告中使用cbed的方法
使用了未定義的型別cbed;
bed->sleep的左邊必須指向類/結構/聯合/泛型型別
class cbed; // 蓋房子時:現在先不買,肯定要買床的
class chouse
};
3、在cbed定義之前呼叫cbed的析構函式
// house.h
class cbed; // 蓋房子時:現在先不買,肯定要買床的
class chouse
};
// house.cpp
#include "bed.h"
#include "house.h" // 等房子開始裝修了,要買床了
chouse::chouse(void)
chouse::~chouse(void)
void chouse::gotobed()
// bed.h
class cbed
;
// bed.cpp
#include "bed.h"
cbed::cbed(void)
cbed::~cbed(void)
void cbed::sleep()
#include "house.h"
int main()
接下來,給出開篇第乙個問題的答案:
// a.h
class b;
class a
;
#include "b.h"
#include "a.h"
a::a(void)
a::~a(void)
// b.h
class a;
class b
;
// b.cpp
#include "a.h"
#include "b.h"
b::b(void)
b::~b(void)
《c++ primer 4edition》在類的友元一章節中說到,如果在乙個類a的宣告中將另乙個類b的成員函式宣告為友元函式f,那麼類a必須事先知道類b的定義;類b的成員函式f宣告如果使用類a作為形參,那麼也必須知道類a的定義,那麼兩個類就互相依賴了。要解決這個問題必須使用類的前置宣告。例如:
// house.h
#include "bed.h"
class chouse
};
// house.cpp
#include "house.h"
chouse::chouse(void)
chouse::~chouse(void)
void chouse::gotobed()
// bed.h
class chouse;
class cbed
;
// bed.cpp
#include "house.h"
cbed::cbed(void)
cbed::~cbed(void)
void cbed::sleep(chouse& h)
C 前置宣告
特點 被宣告的類不用重新編譯,節省編譯時間 比如a包含乙個指向b的指標,b包含a的乙個例項,這種情況下,使用前置宣告。易錯的點 class date class task1 因為分配器為d分配記憶體的時候,必須要知道 d的大小 主要應用場景是兩個標頭檔案相互包含的場景,建議僅將前置宣告用於解決迴圈引...
C 前置宣告
一般的前置函式宣告 見過最多的前置函式宣告,基本格式 如下 1 include 2 using namespace std 34 void fun char ch,int pvalue,double dvalue 56 void main 714 15void fun char ch,int pva...
C 前置宣告
問題 最近遇到了兩個類a b相互呼叫的情況,於是想當然的在兩個類a和b的標頭檔案中 include 了所需的標頭檔案,當然結果編譯報錯了。為什麼呢,a需要b,b需要a,形成了迴圈,違反了程式的確定性原則。如下圖所示 如這樣相互包含的問題,可以使用前置宣告來解決。即 在標頭檔案中宣告該類,在實現檔案中...