程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 更多編程語言 >> 更多關於編程 >> 淺析C++中前置聲明的應用與陷阱

淺析C++中前置聲明的應用與陷阱

編輯:更多關於編程
    以下是對C++中前置聲明的應用與陷阱進行了詳細的分析介紹,需要的朋友參考下  

    前置聲明的使用
    有一定C++開發經驗的朋友可能會遇到這樣的場景:兩個類A與B是強耦合關系,類A要引用B的對象,類B也要引用類A的對象。好的,不難,我的第一直覺讓我寫出這樣的代碼:

    復制代碼 代碼如下:
    // A.h
    #include "B.h"
    class A
    {

    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的定義,就編譯出錯了。提 示信息如下:
    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
    {
        CBed* bed; // 我先給床留個位置
    public:
        CHouse(void);
        virtual ~CHouse(void);
        void GoToBed();
    };
    // House.cpp
    #include "Bed.h"
    #include "House.h" // 等房子開始裝修了,要買床了
    CHouse::CHouse(void)
    {
        bed = new CBed(); // 把床放進房子
    }
    CHouse::~CHouse(void)
    {
    }
    void CHouse::GoToBed()
    {
        bed->Sleep();
    }
    // Bed.h
    class CBed
    {
    public:
        CBed(void);
        ~CBed(void);
        void Sleep();
    };
    // Bed.cpp
    #include "Bed.h"
    CBed::CBed(void)
    {
    }
    CBed::~CBed(void)
    {
    }
    void CBed::Sleep()
    {
    }


    前置聲明中的陷阱
    注意這裡有陷阱:
    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的方法
    使用了未定義的類型CBed;
    bed->Sleep的左邊必須指向類/結構/聯合/泛型類型

    復制代碼 代碼如下:
    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)
    {
    }


    前置聲明在友元類方法中的應用
    《C++ Primer 4Edition》在類的友元一章節中說到,如果在一個類A的聲明中將另一個類B的成員函數聲明為友元函數F,那麼類A必須事先知道類B的定義;類B的成 員函數F聲明如果使用類A作為形參,那麼也必須知道類A的定義,那麼兩個類就互相依賴了。要解決這個問題必須使用類的前置聲明。例如:

    復制代碼 代碼如下:


    // House.h
    #include "Bed.h"
    class CHouse
    {
        friend void CBed::Sleep(CHouse&);
    public:
        CHouse(void);
        virtual ~CHouse(void);
        void GoToBed();
        void RemoveBed()
        {
        }
    };
    // House.cpp
    #include "House.h"
    CHouse::CHouse(void)
    {
    }
    CHouse::~CHouse(void)
    {
        int i = 1;
    }
    void CHouse::GoToBed()
    {
    }
    // Bed.h
    class CHouse;
    class CBed
    {
        int* num;
    public:
        CBed(void);
        ~CBed(void);
        void Sleep(CHouse&);
    };
    // Bed.cpp
    #include "House.h"
    CBed::CBed(void)
    {
        num = new int(1);
    }
    CBed::~CBed(void)
    {
        delete num;
    }
    void CBed::Sleep(CHouse& h)
    {
    }

    1. 上一頁:
    2. 下一頁:
    Copyright © 程式師世界 All Rights Reserved