1. 程式人生 > >C++前置宣告的應用與陷阱

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

一.前置宣告的使用
有一定C++開發經驗的朋友可能會遇到這樣的場景:兩個類A與B是強耦合關係,類A要引用B的物件,類B也要引用類A的物件。好的,不難,我的第一直覺讓我寫出這樣的程式碼:

// A.h
#include "B.h"
class A
{
    B b;
public:
    A(void);
    virtual ~A(void);
};

//A.cpp
#include "A.h"
A::A(void)
{
}


A::~A(void)
{
}

// B.h
#include "A.h"
class B
{
    A a;
public:
    B(void);
    ~B(void
); }; // 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的定義,就編譯出錯了。

那怎麼辦?有辦法,C++為我們提供了前置宣告。前置宣告是什麼?舉個形象點的例子,就是我要蓋一個屋子(CHOuse),光有屋子還不行啊,我還得有床(CBed)。但是屋子還沒蓋好,總不能先買床吧,床的大小我定了,改天買。先得把房子蓋好,蓋房子的時候我先給床留個位置,等房子蓋好了,我再決定買什麼樣的床。前置宣告就是我在定義一個類(CHouse)的時候,用到了另外一個類的定義(CBed),但是CBed還沒有定義呢,而且我還先不需要CBed的定義,只要知道CBed是一個類就夠了。那好,我就先宣告類CBed,告訴編譯器CBed是一個類(不用包含CBed的標頭檔案),然後在CHouse中用到CBed的,都用CBed的指標型別代(因為指標型別固定大小的,但是CBed的大小隻用知道了CBed定義才能確定)。等到要實現CHouse定義的時候,就必須要知道CBed的定義了,那是再包好CBed的標頭檔案就行了。

二.前置宣告的陷阱
1.CBed* bed;必須用指標或引用

// House.h
class CBed; // 蓋房子時:現在先不買,肯定要買床的
class CHouse
{
    CBed& bed; // 我先給床留個位置
    // CBed bed; // 編譯出錯
public:
    CHouse(void);
    CHouse(CBed& bedTmp);
    virtual ~CHouse(void);
    void GoToBed();
};

// House.cpp
#include "Bed.h"
#include "House.h" // 等房子開始裝修了,要買床了
CHouse::CHouse(void) : bed(*new CBed()) { CBed* bedTmp = new CBed(); // 把床放進房子 bed = *bedTmp; } CHouse::CHouse(CBed& bedTmp) : bed(bedTmp) { } CHouse::~CHouse(void) { delete &bed; } void CHouse::GoToBed() { bed.Sleep(); }

2、不能在CHouse的宣告中使用CBed的方法,但是可以在原始檔中CHouse.cpp中使用,因為有#include “Bed.h”
class CBed; // 蓋房子時:現在先不買,肯定要買床的
class CHouse
{
CBed* bed; // 我先給床留個位置
// CBed bed; // 編譯出錯
public:
CHouse(void);
virtual ~CHouse(void);
void GoToBed()
{
bed->Sleep(); // 編譯出錯,床都沒買,怎麼能睡
}
};

3、在CBed定義之前呼叫CBed的解構函式

// House.h
class CBed; // 蓋房子時:現在先不買,肯定要買床的
class CHouse
{
    CBed* bed; // 我先給床留個位置
    // CBed bed; // 編譯出錯
public:
    CHouse(void);
    virtual ~CHouse(void);
    void GoToBed();
    void RemoveBed()
    {
        delete bed; // 我不需要床了,我要把床拆掉。還沒買怎麼拆?
    }
};

// House.cpp
#include "Bed.h"
#include "House.h" // 等房子開始裝修了,要買床了

CHouse::CHouse(void)
{
    bed = new CBed(); // 把床放進房子
}

CHouse::~CHouse(void)
{
    int i = 1;
}

void CHouse::GoToBed()
{
    bed->Sleep();
}

// Bed.h
class CBed
{
    int* num;
public:
    CBed(void);
    ~CBed(void);
    void Sleep();
};

// Bed.cpp
#include "Bed.h"

CBed::CBed(void)
{
    num = new int(1);
}

CBed::~CBed(void)
{
    delete num; // 呼叫不到
}

void CBed::Sleep()
{

}

//main.cpp
#include "House.h"

int main()
{
    CHouse house;
    house.RemoveBed();
}

三.前置宣告解決兩個類的互相依賴
接下來,給出開篇第一個問題的答案:

// A.h
class B;
class A
{
    B* b;
public:
    A(void);
    virtual ~A(void);
};

//A.cpp
#include "B.h"
#include "A.h"
A::A(void)
{
    b = new B;
}


A::~A(void)
{
}

// B.h
class A;
class B
{
    A a;
public:
    B(void);
    ~B(void);
};

// B.cpp
#include "A.h"
#include "B.h"
B::B(void)
{
    a = New A;
}


B::~B(void)
{
}