C 中前置宣告的應用與陷阱

2021-09-09 04:25:47 字數 4286 閱讀 1344

有一定c++開發經驗的朋友可能會遇到這樣的場景:兩個類a與b是強耦合關係,類a要引用b的物件,類b也要引用類a的物件。好的,不難,我的第一直覺讓我寫出這樣的**:

//

a.h#include "

b.h"

classa;

#include "

a.h"

a::a(

void

)a::~a(void)//

b.h#include "

a.h"

classb;

#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

" //注:這個很關鍵在原始檔cpp處是要增加標頭檔案的#include

"house.h"//

等房子開始裝修了,要買床了

chouse::chouse(

void

)chouse::~chouse(void

)void

chouse::gotobed()

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的方法,但是可以在原始檔中chouse.cpp中使用,因為有#include "bed.h"

使用了未定義的型別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()

class

cbed;//

bed.cpp

#include "

bed.h

"cbed::cbed(

void

)cbed::~cbed(void

)void

cbed::sleep()

#include "

house.h

"int

main()

接下來,給出開篇第乙個問題的答案:

//

a.hclass

b;classa;

#include "

b.h"

#include

"a.h

"a::a(

void

)a::~a(void)//

b.hclass

a;classb;

#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()

class

chouse;

class

cbed;//

bed.cpp

#include "

house.h

"cbed::cbed(

void

)cbed::~cbed(void

)void cbed::sleep(chouse&h)

**:

C 中前置宣告的應用與陷阱

有一定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 cl...

C 中前置宣告

有一定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 cl...

關於C 中的前置宣告

關於c 中的前置宣告 在編寫c 程式的時候,偶爾需要用到前置宣告 forward declaration 下面的程式中,帶注釋的那行就是類b的前置說明。這是必須的,因為類a中用到了類b,而類b的宣告出現在類a的後面。如果沒有類b的前置說明,下面的程式將不同通過編譯,編譯器將會給出類似 缺少型別說明符...