日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > c/c++ >内容正文

c/c++

c++ 之类的前置声明

發(fā)布時(shí)間:2024/8/23 c/c++ 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 c++ 之类的前置声明 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

轉(zhuǎn)自:http://blog.csdn.net/fjb2080/archive/2010/04/27/5533514.aspx?作者:清林,博客名:飛空靜渡

剛開(kāi)始學(xué)習(xí)c++的人都會(huì)遇到這樣的問(wèn)題:

定義一個(gè)類 class A,這個(gè)類里面使用了類B的對(duì)象b,然后定義了一個(gè)類B,里面也包含了一個(gè)類A的對(duì)象a,就成了這樣:

?

  • //a.h??
  • #include?"b.h"??
  • class?A??
  • {??
  • ....??
  • private:??
  • ????B?b;??
  • };??
  • //b.h??
  • #include?"a.h"??
  • class?B??
  • {??
  • ....??
  • private:??
  • ????A?a;??
  • };??
  • ?

    一編譯,就出現(xiàn)了一個(gè)互包含的問(wèn)題了,這時(shí)就有人跳出來(lái)說(shuō),這個(gè)問(wèn)題的解決辦法可以這樣,在a.h文件中聲明類B,然后使用B的指針。

    ?

  • //a.h???
  • //#include?"b.h"??
  • class?B;???
  • class?A???
  • {??
  • ?....???
  • private:??
  • ?B?b;???
  • };???
  • //b.h???
  • #include?"a.h"???
  • class?B??
  • {??
  • ?....???
  • private:??
  • ?A?a;???
  • };??
  • ?

    然后,問(wèn)題就解決了。

    但是,有人知道問(wèn)題是為什么就被解決的嗎,也就是說(shuō),加了個(gè)前置聲明為什么就解決了這樣的問(wèn)題。下面,讓我來(lái)探討一下這個(gè)前置聲明。

    類的前置聲明是有許多的好處的。

    我們使用前置聲明的一個(gè)好處是,從上面看到,當(dāng)我們?cè)陬怉使用類B的前置聲明時(shí),我們修改類B時(shí),只需要重新編譯類B,而不需要重新編譯a.h的(當(dāng)然,在真正使用類B時(shí),必須包含b.h)。

    另外一個(gè)好處是減小類A的大小,上面的代碼沒(méi)有體現(xiàn),那么我們來(lái)看下:

    ?

  • //a.h??
  • class?B;??
  • class?A??
  • {??
  • ????....??
  • private:??
  • ????B?*b;??
  • ....??
  • };??
  • //b.h??
  • class?B??
  • {??
  • ....??
  • private:??
  • ????int?a;??
  • ????int?b;??
  • ????int?c;??
  • };??
  • ?

    我們看上面的代碼,類B的大小是12(在32位機(jī)子上)。

    如果我們?cè)陬怉中包含的是B的對(duì)象,那么類A的大小就是12(假設(shè)沒(méi)有其它成員變量和虛函數(shù))。如果包含的是類B的指針*b變量,那么類A的大小就是4,所以這樣是可以減少類A的大小的,特別是對(duì)于在STL的容器里包含的是類的對(duì)象而不是指針的時(shí)候,這個(gè)就特別有用了。

    在前置聲明時(shí),我們只能使用的就是類的指針和引用(因?yàn)橐靡彩蔷佑谥羔樀膶?shí)現(xiàn)的)。

    那么,我問(wèn)你一個(gè)問(wèn)題,為什么我們前置聲明時(shí),只能使用類型的指針和引用呢?

    如果你回答到:那是因?yàn)橹羔樖枪潭ù笮?#xff0c;并且可以表示任意的類型,那么可以給你80分了。為什么只有80分,因?yàn)檫€沒(méi)有完全回答到。

    想要更詳細(xì)的答案,我們看下下面這個(gè)類:

    ?

  • class?A??
  • {??
  • public:??
  • ????A(int?a):_a(a),_b(_a){}?//?_b?is?new?add??
  • ??????
  • ????int?get_a()?const?{return?_a;}??
  • ????int?get_b()?const?{return?_b;}?//?new?add??
  • private:??
  • ????int?_b;?//?new?add??
  • ????int?_a;??
  • };??
  • ?

    我們看下上面定義的這個(gè)類A,其中_b變量和get_b()函數(shù)是新增加進(jìn)這個(gè)類的。

    那么我問(wèn)你,在增加進(jìn)_b變量和get_b()成員函數(shù)后這個(gè)類發(fā)生了什么改變,思考一下再回答。

    好了,我們來(lái)列舉這些改變:

    第一個(gè)改變當(dāng)然是增加了_b變量和get_b()成員函數(shù);

    第二個(gè)改變是這個(gè)類的大小改變了,原來(lái)是4,現(xiàn)在是8。

    第三個(gè)改變是成員_a的偏移地址改變了,原來(lái)相對(duì)于類的偏移是0,現(xiàn)在是4了。

    上面的改變都是我們顯式的、看得到的改變。還有一個(gè)隱藏的改變,想想是什么。。。

    這個(gè)隱藏的改變是類A的默認(rèn)構(gòu)造函數(shù)和默認(rèn)拷貝構(gòu)造函數(shù)發(fā)生了改變。

    由上面的改變可以看到,任何調(diào)用類A的成員變量或成員函數(shù)的行為都需要改變,因此,我們的a.h需要重新編譯。

    如果我們的b.h是這樣的:

    ?

  • //b.h??
  • #include?"a.h"??
  • class?B??
  • {??
  • ...??
  • private:??
  • ????A?a;??
  • };??
  • ?

    那么我們的b.h也需要重新編譯。

    如果是這樣的:

    ?

  • //b.h??
  • class?A;??
  • class?B??
  • {??
  • ...??
  • private:??
  • ????A?*a;??
  • };??
  • ?

    那么我們的b.h就不需要重新編譯。

    像我們這樣前置聲明類A:

    class A;

    是一種不完整的聲明,只要類B中沒(méi)有執(zhí)行需要了解類A的大小或者成員的操作,則這樣的不完整聲明允許聲明指向A的指針和引用。

    而在前一個(gè)代碼中的語(yǔ)句

    A a;

    是需要了解A的大小的,不然是不可能知道如果給類B分配內(nèi)存大小的,因此不完整的前置聲明就不行,必須要包含a.h來(lái)獲得類A的大小,同時(shí)也要重新編譯類B。

    再回到前面的問(wèn)題,使用前置聲明只允許的聲明是指針或引用的一個(gè)原因是只要這個(gè)聲明沒(méi)有執(zhí)行需要了解類A的大小或者成員的操作就可以了,所以聲明成指針或引用是沒(méi)有執(zhí)行需要了解類A的大小或者成員的操作的。

    ?

    ?

    轉(zhuǎn)自:http://blog.csdn.net/rogeryi/archive/2006/12/12/1439597.aspx

    這篇文章很大程度是受到Exceptional C++ (Hurb99)書(shū)中第四章 Compiler? Firewalls and the Pimpl Idiom? (編譯器防火墻和Pimpl慣用法) 的啟發(fā),這一章講述了減少編譯時(shí)依賴的意義和一些慣用法,其實(shí)最為常用又無(wú)任何副作用的是使用前置聲明來(lái)取代包括頭文件。

    Item 26 的Guideline - "Never #include a header when a forward declaration will suffice"

    在這里,我自己總結(jié)了可以使用前置聲明來(lái)取代包括頭文件的各種情況和給出一些示例代碼。

    首先,我們?yōu)槭裁匆^文件?問(wèn)題的回答很簡(jiǎn)單,通常是我們需要獲得某個(gè)類型的定義(definition)。那么接下來(lái)的問(wèn)題就是,在什么情況下我們才需要類型的定義,在什么情況下我們只需要聲明就足夠了?問(wèn)題的回答是當(dāng)我們需要知道這個(gè)類型的大小或者需要知道它的函數(shù)簽名的時(shí)候,我們就需要獲得它的定義。

    假設(shè)我們有類型A和類型C,在哪些情況下在A需要C的定義:

  • A繼承至C
  • A有一個(gè)類型為C的成員變量
  • A有一個(gè)類型為C的指針的成員變量
  • A有一個(gè)類型為C的引用的成員變量
  • A有一個(gè)類型為std::list<C>的成員變量
  • A有一個(gè)函數(shù),它的簽名中參數(shù)和返回值都是類型C
  • A有一個(gè)函數(shù),它的簽名中參數(shù)和返回值都是類型C,它調(diào)用了C的某個(gè)函數(shù),代碼在頭文件中
  • A有一個(gè)函數(shù),它的簽名中參數(shù)和返回值都是類型C(包括類型C本身,C的引用類型和C的指針類型),并且它會(huì)調(diào)用另外一個(gè)使用C的函數(shù),代碼直接寫(xiě)在A的頭文件中
  • C和A在同一個(gè)名字空間里面
  • C和A在不同的名字空間里面

  • 1,沒(méi)有任何辦法,必須要獲得C的定義,因?yàn)槲覀儽仨氁繡的成員變量,成員函數(shù)。

    2,需要C的定義,因?yàn)槲覀円繡的大小來(lái)確定A的大小,但是可以使用Pimpl慣用法來(lái)改善這一點(diǎn),詳情請(qǐng)
    看Hurb的Exceptional C++。

    3,4,不需要,前置聲明就可以了,其實(shí)3和4是一樣的,引用在物理上也是一個(gè)指針,它的大小根據(jù)平臺(tái)不同,可能是32位也可能是64位,反正我們不需要知道C的定義就可以確定這個(gè)成員變量的大小。

    5,不需要,有可能老式的編譯器需要。標(biāo)準(zhǔn)庫(kù)里面的容器像list, vector,map,
    在包括一個(gè)list<C>,vector<C>,map<C, C>類型的成員變量的時(shí)候,都不需要C的定義。因?yàn)樗鼈儍?nèi)部其實(shí)也是使用C的指針作為成員變量,它們的大小一開(kāi)始就是固定的了,不會(huì)根據(jù)模版參數(shù)的不同而改變。

    6,不需要,只要我們沒(méi)有使用到C。

    7,需要,我們需要知道調(diào)用函數(shù)的簽名。

    8,8的情況比較復(fù)雜,直接看代碼會(huì)比較清楚一些。

    ????????????C&?doToC(C&);
    ????????????C&?doToC2(C&?c)?{return?doToC(c);};


    從上面的代碼來(lái)看,A的一個(gè)成員函數(shù)doToC2調(diào)用了另外一個(gè)成員函數(shù)doToC,但是無(wú)論是doToC2,還是doToC,它們的的參數(shù)和返回類型其實(shí)都是C的引用(換成指針,情況也一樣),引用的賦值跟指針的賦值都是一樣,無(wú)非就是整形的賦值,所以這里即不需要知道C的大小也沒(méi)有調(diào)用C的任何函數(shù),實(shí)際上這里并不需要C的定義。

    但是,我們隨便把其中一個(gè)C&換成C,比如像下面的幾種示例:

    ??????????? 1.
    ??? ??? ??? ??? C&?doToC(C&);
    ????????????C&?doToC2(C?c)?{return?doToC(c);};
    ??? ?? ?? ?? ???
    ??? ?? ?? ?? ?? 2.
    ??????????? ??? C& doToC(C);
    ??????????? ??? C& doToC2(C& c) {return doToC(c);};

    ??? ?? ?? ?? ?? 3.
    ??? ?? ?? ?? ???C?doToC(C&);
    ??????????? ??? C& doToC2(C& c) {return doToC(c);};

    ??? ?? ?? ?? ?? 4.
    ??? ?? ?? ?? ?? C& doToC(C&);
    ??????????? ????C?doToC2(C& c) {return doToC(c);};


    無(wú)論哪一種,其實(shí)都隱式包含了一個(gè)拷貝構(gòu)造函數(shù)的調(diào)用,比如1中參數(shù)c由拷貝構(gòu)造函數(shù)生成,3中doToC的返回值是一個(gè)由拷貝構(gòu)造函數(shù)生成的匿名對(duì)象。因?yàn)槲覀冋{(diào)用了C的拷貝構(gòu)造函數(shù),所以以上無(wú)論那種情形都需要知道C的定義。

    9和10都一樣,我們都不需要知道C的定義,只是10的情況下,前置聲明的語(yǔ)法會(huì)稍微復(fù)雜一些。
    最后給出一個(gè)完整的例子,我們可以看到在兩個(gè)不同名字空間的類型A和C,A是如何使用前置聲明來(lái)取代直接包括C的頭文件的:
    A.h

    Cpp代碼??
  • #pragma?once??
  • ??
  • #include?<list>??
  • #include?<vector>??
  • #include?<map>??
  • #include?<utility>??
  • ??
  • ????//不同名字空間的前置聲明方式??
  • namespace?test1??
  • {??
  • ??????????class?C;??
  • }??
  • ??
  • ??
  • ??
  • namespace?test2??
  • {?????
  • ???????//用using避免使用完全限定名??
  • ????using?test1::C;??
  • ??????
  • ????class?A???
  • ????{??
  • ????public:??
  • ????????????????C???useC(C);??
  • ????????????C&?doToC(C&);??
  • ????????????C&?doToC2(C&?c)?{return?doToC(c);};??
  • ???????????????????????????
  • ????private:??
  • ????????????std::list<C>????_list;??
  • ????????????std::vector<C>??_vector;??
  • ????????????std::map<C,?C>??_map;??
  • ????????????C*??????????????_pc;??
  • ????????????C&??????????????_rc;??
  • ??????
  • ????};??
  • } ?
  • 創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來(lái)咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)

    總結(jié)

    以上是生活随笔為你收集整理的c++ 之类的前置声明的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。