C 中前置宣告

2021-08-21 15:36:19 字數 4207 閱讀 8363

有一定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,形成了迴圈,違反了程式的確定性原則。如下圖所示 如這樣相互包含的問題,可以使用前置宣告來解決。即 在標頭檔案中宣告該類,在實現檔案中...