• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            Note of Justin

            關(guān)于工作和讀書(shū)的筆記

              C++博客 :: 首頁(yè) :: 聯(lián)系 :: 聚合  :: 管理
              47 Posts :: 0 Stories :: 45 Comments :: 0 Trackbacks

            留言簿(14)

            搜索

            •  

            積分與排名

            • 積分 - 52490
            • 排名 - 433

            最新評(píng)論

            閱讀排行榜

            評(píng)論排行榜

            [原創(chuàng)文章歡迎轉(zhuǎn)載,但請(qǐng)保留作者信息]
            Justin 于 2009-12-20

            大師說(shuō)了,C++的設(shè)計(jì)還是有缺陷的:它無(wú)法把接口(interface)的設(shè)計(jì)和實(shí)現(xiàn)(implementation)的設(shè)計(jì)完全劃分開(kāi)來(lái)。比如說(shuō)在一個(gè)類的(接口)聲明當(dāng)中,總是或多或少的會(huì)泄漏一些實(shí)現(xiàn)上的細(xì)節(jié),雖然這樣做與接口的設(shè)計(jì)并沒(méi)有太多聯(lián)系。
            有同學(xué)說(shuō)應(yīng)該多放些代碼一起炒冷飯,是個(gè)好主意,下面是書(shū)中的修改版本,大致是一樣的。

            class ?AClass {
            public :
            ???
            void ?interface_1();
            ???std::
            string ?interface_2();
            ???
            // ..
            private :
            ???
            // ?implementation?details?are?leaking?as?below..
            ???std:: string ?internalData_1;
            ???BClass?internalData_2;
            ???
            // ..
            }

            這些實(shí)現(xiàn)上的細(xì)節(jié)往往需要引用其他頭文件中相關(guān)對(duì)象的定義(比如說(shuō)下面的代碼),從而產(chǎn)生了對(duì)這些頭文件的(在編譯時(shí)的)依賴。因此每次這些文件中的某個(gè)有變化時(shí),依賴它的所有文件都需要重新編譯。

            #include? < string >
            #include?
            " BClass.h "
            // ..

            【注意】這里貌似邏輯不是很順:就算沒(méi)有那些私有成員的聲明,接口函數(shù)的返回值如果是string或是BClass等類型,不還是一樣需要依賴引用其他頭文件嗎?其實(shí)這是兩種不一樣的情況,實(shí)現(xiàn)和接口。前面說(shuō)的實(shí)現(xiàn)細(xì)節(jié)的泄漏是會(huì)導(dǎo)致編譯依賴的,因?yàn)榫幾g器需要了解這些類型對(duì)象的大小進(jìn)而為其分配內(nèi)存空間;但是接口,比如說(shuō)函數(shù)的返回值或是參數(shù)表中的參數(shù),就不需要編譯器去考慮分配內(nèi)存的問(wèn)題,因此也就沒(méi)有所謂的編譯依賴了。
            問(wèn)題知道了,那么解決辦法呢,大師提出“骨肉分離法”(嗯……其實(shí)是我的杜撰@#¥%):將聲明(declaration)和定義(definition)分開(kāi)。

            呃……下面的比喻,最好吃完飯?jiān)倮^續(xù)。
            如果說(shuō)接口是一個(gè)類的骨架,那么實(shí)現(xiàn)就是他的血肉;如果說(shuō)聲明讓你摸到了骨頭,那么定義應(yīng)該就是血和肉生長(zhǎng)的地方。
            根據(jù)骨肉分離法,對(duì)于一個(gè)AClass類,第一步先把血肉(定義/實(shí)現(xiàn))剝離開(kāi),只留下骨架。然后找個(gè)盒子(新建一個(gè)類,比如說(shuō)AClassImpl),把血肉放進(jìn)去。
            接下來(lái)還有一步,在骨頭盒子里(原AClass類)加一條繩子連著血肉盒子(一個(gè)指向AClassImpl的指針),這樣才不至于讓骨肉真正的分離,只要找到了骨頭盒子,就一定能找到血肉盒子,然后對(duì)于這個(gè)“可憐”的AClass來(lái)說(shuō),它的全部“零件”都是完整的,啥也沒(méi)丟,但是做到了骨肉分離。

            也做到了沒(méi)有編譯依賴。
            因?yàn)閷?duì)于AClass的用戶來(lái)說(shuō),他們面對(duì)的將是一個(gè)沒(méi)有定義的類,這個(gè)類的后繼改動(dòng),只要不涉及接口的改動(dòng),都不會(huì)導(dǎo)致用戶程序的重新編譯。
            看到這里想想工作時(shí)看到的代碼,原來(lái)前輩也有看過(guò)啊……
            對(duì)比前面的例程,給一個(gè)“骨肉分離”了的版本吧:

            class ?AClassImpl {
            // ..
            private :
            ???
            // ?implementation?details?are?moved?here..
            ???std:: string ?internalData_1;
            ???BClass?internalData_2;
            // ..
            }


            class ?AClass {
            public :
            ???
            void ?interface_1();
            ???std::
            string ?interface_2();
            ???
            // ..
            private :
            ???
            // ?there?is?only?a?pointer?to?implementation
            ???std::tr1::shared_ptr < AClassImpl > ?pImpl;
            }


            // a?constructor:?instantiations?of?AClass?and?AClassImpl?should?always?be?bound?together.
            AClass::AClass( // ..)?:?pImpl(new?AClassImpl( // ..))
            {
            ???
            // ..
            }

            前面的文字是自己的理解,而大師的真言是這樣的:

            • 如果可以用指針/引用的話,就不用對(duì)象。
            • 如果可以做到僅依賴聲明,就不要依賴定義。
            • 為定義和聲明分別準(zhǔn)備兩個(gè)頭文件。這樣一來(lái),用戶就可以很簡(jiǎn)單做到上面兩點(diǎn)。

            如果覺(jué)得骨肉分離太殘忍,大師還有另外一個(gè)工具:工廠(factory)。
            第二種方法中,抽象類/接口類提供了所有接口的純虛函數(shù)形式:會(huì)有該類的子類去實(shí)現(xiàn)這些接口。與此同時(shí),在抽象類/接口類中還會(huì)有一個(gè)靜態(tài)(static)的工廠函數(shù)(比如create()/produce()/factory()……),這個(gè)函數(shù)實(shí)際上起到了構(gòu)造函數(shù)的作用,它“制造”出子類對(duì)象來(lái)完成真正的任務(wù),同時(shí)返回這個(gè)對(duì)象的指針(通常是智能指針如shared_ptr)。憑借這個(gè)返回的指針就可以進(jìn)行正常的操作,同時(shí)不會(huì)有編譯依賴的擔(dān)心。一個(gè)簡(jiǎn)陋的代碼見(jiàn)下:

            class ?AClass: public ?AClassFactory {
            public :
            ???AClass()?
            {}
            ???
            void ?interface_1();
            ???std::
            string ?interface_2();
            ???
            virtual ? ~ AClass();
            // ..
            }


            class ?AClassFactory {
            public :
            ???
            virtual ? void ?interface_1()? = ? 0 ;
            ???
            virtual ?std:: string ?interface_2()? = ? 0 ;
            ???
            // ..
            ??? virtual ? ~ AClassFactory() { /* .. */ }
            ???
            static ?std::tr1::shared_ptr < AClassFactory > ?Produce( /* .. */ )
            ???
            {
            ??????
            // this?factory?function?could?be?more?complicated?in?practice..
            ?????? return ?std::tr1::shared_ptr < AClassFactory > ( new ?AClass);
            ???}

            // ..
            }



            // AClassFactory?could?be?used?in?this?way..
            std::tr1::shared_ptr < AClassFactory > ?pAClassObject;
            pAClassObject?
            = ?AClassFactory::Produce( /* .. */ );
            // pAClassObject->..

            無(wú)論是骨肉分離法還是工廠模式,都可以去除編譯依賴。代價(jià)是有的,要為之付出一點(diǎn)點(diǎn)額外代碼執(zhí)行的時(shí)間和空間。這個(gè)代價(jià)又可以通過(guò)內(nèi)聯(lián)函數(shù)(inline function)來(lái)減小一些。(不過(guò)有聽(tīng)過(guò)這種說(shuō)法:大部分的編譯器都會(huì)將短小的函數(shù)自動(dòng)轉(zhuǎn)成內(nèi)聯(lián)函數(shù)的)
            盡管如此,只有在以上做法很明顯地降低了系統(tǒng)的性能的情況下,才可以放棄分離實(shí)現(xiàn)和接口的努力。
            這是大師的忠告。
            posted on 2010-02-01 09:06 Justin.H 閱讀(2064) 評(píng)論(2)  編輯 收藏 引用 所屬分類: Effective C++ 炒冷飯

            Feedback

            # re: 讀書(shū)筆記:Effective C++ 炒冷飯 - Item 31 減少文件間的編譯依賴 2012-10-02 20:34 xiaolong
            只能說(shuō)降低編譯依賴,但是實(shí)際工作中編譯依賴簡(jiǎn)直太大了,而且很多情況下無(wú)法避免的吧!  回復(fù)  更多評(píng)論
              

            # re: 讀書(shū)筆記:Effective C++ 炒冷飯 - Item 31 減少文件間的編譯依賴[未登錄](méi) 2014-11-18 17:00 liu
            寫(xiě)的不錯(cuò),血肉的說(shuō)法太瘆人了,改成肉吧  回復(fù)  更多評(píng)論
              

            99久久综合狠狠综合久久止| 国内精品人妻无码久久久影院 | 久久精品国产亚洲AV忘忧草18| 亚洲国产二区三区久久| 日本精品一区二区久久久| 亚洲国产精久久久久久久| 国产精品久久久久久搜索| 国产精品一区二区久久国产| 久久天天躁狠狠躁夜夜躁2O2O| 久久精品国产久精国产一老狼| 日产精品久久久久久久| 狠狠色丁香久久婷婷综合图片| 久久精品国产99国产精品导航| 久久精品国产亚洲av麻豆图片 | 久久精品国产影库免费看| 人妻无码精品久久亚瑟影视| 国产A级毛片久久久精品毛片| 久久精品人人做人人爽电影| 久久夜色精品国产噜噜噜亚洲AV| 久久国产色av免费看| 性色欲网站人妻丰满中文久久不卡| 久久无码AV一区二区三区| 亚洲人成伊人成综合网久久久| 久久久噜噜噜久久熟女AA片| 久久夜色精品国产亚洲| 久久噜噜久久久精品66| 国内精品综合久久久40p| 国产综合久久久久久鬼色| 久久精品国产99久久久香蕉| 久久亚洲国产成人影院| 久久婷婷五月综合97色一本一本| 精品综合久久久久久97超人| 国产精品久久久99| 久久久久久精品成人免费图片| 国产精品久久久久国产A级| 久久91这里精品国产2020| 亚洲欧美日韩精品久久亚洲区| 亚洲日本va中文字幕久久| 99久久免费国产精品热| 中文国产成人精品久久亚洲精品AⅤ无码精品 | 久久人人爽人人爽人人片AV不|