• <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>

            洛譯小筑

            別來(lái)無(wú)恙,我的老友…
            隨筆 - 45, 文章 - 0, 評(píng)論 - 172, 引用 - 0
            數(shù)據(jù)加載中……

            [ECPP讀書(shū)筆記 條目35] 虛函數(shù)的替代方案

            假設(shè)你正在設(shè)計(jì)一款游戲軟件,你需要為游戲中的各種角色設(shè)計(jì)一個(gè)層次結(jié)構(gòu)。你游戲的劇情中充滿了風(fēng)險(xiǎn)刺激,那么游戲中的角色就會(huì)經(jīng)常遇到受傷或者生命值降低的情況。于是你決定為角色類(lèi)提供一個(gè)成員函數(shù):healthValue,這一函數(shù)通過(guò)返回一個(gè)整數(shù)值來(lái)表示角色當(dāng)前的生命值。由于計(jì)算不同角色生命值的方式可能會(huì)不一樣,因此不妨將healthValue聲明為虛函數(shù),這樣做十分順理成章:

            class GameCharacter {

            public:

              virtual int healthValue() const; // 返回角色的生命值

              ...                              // 派生類(lèi)可以重定義該函數(shù)

            };

            此處,healthValue函數(shù)并沒(méi)有直接聲明為純虛函數(shù),這一事實(shí)告訴我們計(jì)算生命值存在著一種默認(rèn)的算法(參見(jiàn)條目34)。

            事實(shí)上,所謂“順理成章”的方法,在某些情況下卻會(huì)成為工作的絆腳石。由于這一設(shè)計(jì)太過(guò)“順理成章”了,你也許就不會(huì)再為尋找替代方案花費(fèi)足夠的精力。為了讓你從面向?qū)ο笤O(shè)計(jì)的思維定勢(shì)中解脫出來(lái),讓我們另辟蹊徑,解決這一問(wèn)題。

            模板方法模式:通過(guò)“非虛接口慣用法”實(shí)現(xiàn)

            我們從討論一個(gè)有趣的思想流派開(kāi)始,這一流派堅(jiān)持虛函數(shù)必定為私有函數(shù)。其追捧者也許會(huì)提出,更優(yōu)秀的方案中healthValue仍為公共成員函數(shù),但將其聲明為非虛函數(shù),并讓其調(diào)用一個(gè)私有的虛函數(shù),真正的工作由這個(gè)虛函數(shù)來(lái)完成。不妨將其命名為doHealthValue

            class GameCharacter {

            public:

              int healthValue() const            // 派生類(lèi)不能對(duì)其進(jìn)行重定義

              {                                   // 參見(jiàn)條目36

                ...                               // 準(zhǔn)備工作:參見(jiàn)下文

                int retVal = doHealthValue();     // 進(jìn)行實(shí)際的工作

                ...                               // 后期工作:參見(jiàn)下文 

                return retVal;

              }

              ...

             

            private:

              virtual int doHealthValue() const  // 派生類(lèi)可以對(duì)其進(jìn)行重定義

              {

                ...                               // 計(jì)算角色生命值的默認(rèn)算法

              }

            };

            上文的代碼中(以及本條目中所有其它代碼中),我將成員函數(shù)的定義內(nèi)容放置在了類(lèi)定義中。如同條目30中所解釋的,這樣做使得這些函數(shù)隱式帶有內(nèi)聯(lián)屬性。我這樣做只是為了讓所說(shuō)明的問(wèn)題更加簡(jiǎn)單明了。是否選擇內(nèi)聯(lián)與本條款的設(shè)計(jì)方案無(wú)關(guān),因此不要認(rèn)為這里在類(lèi)的內(nèi)部編寫(xiě)成員函數(shù)是出于什么目的的。不要誤會(huì)了。

            讓客戶(hù)通過(guò)調(diào)用公有的非虛成員函數(shù)來(lái)間接的調(diào)用私有虛函數(shù)——這是一個(gè)基本的設(shè)計(jì)方案。我們一般將其稱(chēng)為“非虛擬接口慣用法(non-virtual interface,簡(jiǎn)寫(xiě)為NVI)” 。這一方案是一個(gè)更為一般化的設(shè)計(jì)模式“模板方法”(可惜這一模式和C++中的模板并沒(méi)有什么關(guān)系)的一個(gè)特定的表現(xiàn)形式。我們將這些非虛函數(shù)(比如上文中的healthValue)稱(chēng)為虛函數(shù)的“包裝器”。

            請(qǐng)留意上文代碼中的“準(zhǔn)備工作”、“后期工作”這兩段注釋內(nèi)容。在虛函數(shù)做實(shí)際工作的前后,這兩段注釋處的代碼肯定會(huì)得到調(diào)用。這是NVI慣用法的一個(gè)優(yōu)勢(shì),這樣做意味著在程序調(diào)用虛函數(shù)之前,相關(guān)的背景工作已經(jīng)設(shè)定好了;在調(diào)用虛函數(shù)以后,后臺(tái)清理工作也能得以進(jìn)行,這兩項(xiàng)工作都是由包裝器確保進(jìn)行的。舉例說(shuō),“準(zhǔn)備工作”可能包含互斥鎖加鎖、日志記錄、確認(rèn)當(dāng)前類(lèi)的約束條件和函數(shù)的先決條件是否滿足,等等。“后期工作”可能包括互斥鎖解鎖、檢查函數(shù)的后置條件、重新檢查類(lèi)的約束條件,等等。如果你讓客戶(hù)直接調(diào)用虛函數(shù)的話,那么這些工作也就很難開(kāi)展了。

            你也許注意到了:在使用NVI慣用法時(shí),我們可以在派生類(lèi)中對(duì)私有虛函數(shù)進(jìn)行重定義,而這些函數(shù)在派生類(lèi)中是無(wú)法調(diào)用的啊!這一點(diǎn)看上去匪夷所思,但實(shí)際上這里并不存在設(shè)計(jì)上的矛盾。虛函數(shù)的重定義工作確認(rèn)了它實(shí)現(xiàn)其功能的方式,虛函數(shù)的調(diào)用工作確認(rèn)了它實(shí)現(xiàn)其功能的時(shí)機(jī)。兩者是相互獨(dú)立的:NVI慣用法允許在派生類(lèi)中重定義虛函數(shù),這樣做使得基類(lèi)將虛函數(shù)調(diào)用方式的選擇權(quán)賦予派生類(lèi),然而基類(lèi)仍保留函數(shù)調(diào)用時(shí)機(jī)的決定權(quán)。這一點(diǎn)乍看上去有些異樣,但是,“允許派生類(lèi)對(duì)私有虛函數(shù)進(jìn)行重定義”這一C++規(guī)則是非常合理的。

            在NVI慣用法的背景下,并沒(méi)有嚴(yán)格要求虛函數(shù)必須是私有的。在一些類(lèi)層次結(jié)構(gòu)中,一個(gè)虛函數(shù)可能在某個(gè)派生類(lèi)中的實(shí)現(xiàn)版本中調(diào)用基類(lèi)的其他成員(參見(jiàn)條目27中SpecialWindow的例子),為了讓這樣的調(diào)用合法,這個(gè)虛函數(shù)必須聲明為受保護(hù)的,而不是私有的。還有一些情況下虛函數(shù)甚至要聲明為公有的(參見(jiàn)條目7:多態(tài)基類(lèi)中的析構(gòu)函數(shù)),在這些情況下NVI慣用法不再可用。

            策略模式:通過(guò)函數(shù)指針實(shí)現(xiàn)

            NVI慣用法是公有虛函數(shù)的一個(gè)有趣的替代方案,但是從設(shè)計(jì)的觀點(diǎn)來(lái)看,這樣做無(wú)異于粉飾門(mén)面罷了。畢竟我們?nèi)匀辉谑褂锰摵瘮?shù)來(lái)計(jì)算每一個(gè)角色的生命值。這里存在著一個(gè)改進(jìn)效果更為顯著的設(shè)計(jì)主張:計(jì)算角色生命值的工作應(yīng)與角色類(lèi)型完全無(wú)關(guān),于是計(jì)算生命值的工作便無(wú)須為角色類(lèi)的成員。舉例說(shuō),我們可以讓每個(gè)角色類(lèi)的構(gòu)造函數(shù)包含一個(gè)函數(shù)指針參數(shù),并由這一參數(shù)將生命值計(jì)算方法函數(shù)傳入,我們可以通過(guò)調(diào)用這個(gè)函數(shù)進(jìn)行實(shí)際的計(jì)算工作:

            class GameCharacter;               // 前置聲明

             

            // defaultHealthCalc函數(shù):計(jì)算生命值的默認(rèn)算法

            int defaultHealthCalc(const GameCharacter& gc);

             

            class GameCharacter {

            public:

              typedef int (*HealthCalcFunc)(const GameCharacter&);

             

              explicit GameCharacter(HealthCalcFunc hcf = defaultHealthCalc)

              : healthFunc(hcf)

              {}

             

              int healthValue() const

              { return healthFunc(*this); }

              ...

            private:

              HealthCalcFunc healthFunc;

            }; 

            這一方案是對(duì)另一個(gè)常見(jiàn)的設(shè)計(jì)模式——“策略模式”的一個(gè)簡(jiǎn)單應(yīng)用。與使用虛函數(shù)的GameCharacter層次結(jié)構(gòu)解決方案相比而言,本方案為我們帶來(lái)了一些有趣的靈活性:

            同一角色類(lèi)型的不同實(shí)例可以使用不同的生命值計(jì)算函數(shù)。比如:

            class EvilBadGuy: public GameCharacter {

            public:

              explicit EvilBadGuy(HealthCalcFunc hcf = defaultHealthCalc)

              : GameCharacter(hcf)

              { ... }

              ...

            };

            int loseHealthQuickly(const GameCharacter&);  // 不同計(jì)算方式的

            int loseHealthSlowly(const GameCharacter&);   // 生命值計(jì)算函數(shù)

             

            EvilBadGuy ebg1(loseHealthQuickly);           // 同一類(lèi)型的角色

            EvilBadGuy ebg2(loseHealthSlowly);            // 使用不同類(lèi)型的

                                                          // 生命值計(jì)算方法

            對(duì)特定的角色的生命值計(jì)算函數(shù)可以在運(yùn)行時(shí)更改。比如說(shuō),GameCharacter可以提供一個(gè)名為setHealthCalculator的成員函數(shù),我們可以使用這個(gè)函數(shù)來(lái)更換當(dāng)前的生命值計(jì)算函數(shù)。

            另一方面,我們發(fā)現(xiàn),生命值計(jì)算函數(shù)不再是GameCharacter層次結(jié)構(gòu)的成員函數(shù)了,這一事實(shí)說(shuō)明,這類(lèi)生命值計(jì)算函數(shù)不再有訪問(wèn)它們所處理對(duì)象的內(nèi)部成員的特權(quán)。比如說(shuō),defaultHealthCalc函數(shù)對(duì)于EvilBadGuy類(lèi)中的非公有成員就沒(méi)有訪問(wèn)權(quán)限。如果角色的生命值可以單純依靠角色類(lèi)的公有接口所得到的信息來(lái)計(jì)算的話,上述的情況并不是問(wèn)題,但是一旦精確的生命值計(jì)算需要調(diào)用非公有的信息,那么這一情況便成為問(wèn)題了。事實(shí)上,凡是你使用類(lèi)外部的等價(jià)功能(比如通過(guò)非成員非友元函數(shù)或通過(guò)其它類(lèi)的非友元的成員函數(shù))來(lái)替代類(lèi)內(nèi)部的功能(比如通過(guò)成員函數(shù))時(shí),都存在潛在的問(wèn)題。本篇余下部分內(nèi)容都存在這一問(wèn)題,因?yàn)槲覀円紤]的其它設(shè)計(jì)方案都會(huì)涉及到GameCharacter層次結(jié)構(gòu)外部函數(shù)的使用。

            讓非成員函數(shù)訪問(wèn)類(lèi)內(nèi)部的非公有成員只有一個(gè)解決辦法,那就是降低類(lèi)的封裝度,這是一個(gè)一般的規(guī)則。比如說(shuō),類(lèi)可以將非成員函數(shù)生命為友元,類(lèi)也可以為需要隱藏的具體實(shí)現(xiàn)提供公有訪問(wèn)函數(shù)。使用函數(shù)指針代替虛函數(shù)可以帶來(lái)一些優(yōu)勢(shì)(比如可以為每一個(gè)角色的實(shí)例提供一個(gè)生命值計(jì)算函數(shù),可以在運(yùn)行時(shí)更換計(jì)算函數(shù)),而這樣做也會(huì)降低GameCharacter的封裝度,至于優(yōu)缺點(diǎn)之間孰輕孰重,你在實(shí)戰(zhàn)中必須做到具體問(wèn)題具體分析,盡可能的審時(shí)度勢(shì)。

            策略模式:通過(guò)tr1::function實(shí)現(xiàn)

            如果你對(duì)模板和模板的隱式接口的用法(參見(jiàn)條目41)很熟悉的話,那么上述的“基于函數(shù)指針”的方案就顯得十分蹩腳了。我們考慮:舊的方案必須使用一個(gè)函數(shù)來(lái)實(shí)現(xiàn)生命值計(jì)算的功能,能不能用其它一些東西(比如函數(shù)對(duì)象)來(lái)代替這個(gè)函數(shù)呢?為什么這個(gè)函數(shù)不能是成員函數(shù)呢?還有,為什么這個(gè)函數(shù)一定要返回一個(gè)整數(shù)值,而不能返回一個(gè)可以轉(zhuǎn)換成int類(lèi)型的其他對(duì)象呢?

            如果我們使用一個(gè)tr1::function類(lèi)型的對(duì)象來(lái)代替函數(shù)指針,那么上述問(wèn)題中的約束則會(huì)瞬間瓦解。就像條目54中所解釋的,tr1::function對(duì)象可以保存任意可調(diào)用實(shí)體(也就是:函數(shù)指針、函數(shù)對(duì)象、或成員函數(shù)指針),這些實(shí)體的簽名一定與所期待內(nèi)容的類(lèi)型兼容。以下是我們剛剛看到的設(shè)計(jì)方案,這次加入了tr1::function的使用:

            class GameCharacter;               // 同上

            int defaultHealthCalc(const GameCharacter& gc);

                                               // 同上

            class GameCharacter {

            public:

            // HealthCalcFunc可以是任意可調(diào)用的實(shí)體,

            // 它可以被任意與GameCharacter類(lèi)型兼容的對(duì)象調(diào)用

            // 且返回值必與int類(lèi)型兼容,詳情見(jiàn)下文

               typedef std::tr1::function<int (const GameCharacter&)>

                         HealthCalcFunc;

               explicit GameCharacter(HealthCalcFunc hcf = defaultHealthCalc)

               : healthFunc(hcf)

               {}

             

               int healthValue() const

               { return healthFunc(*this); }

             

               ...

             

            private:

              HealthCalcFunc healthFunc;

            };

            正如你所見(jiàn)到的,HealthCalcFunc是對(duì)tr1::function某個(gè)實(shí)例的一個(gè)typedef。這意味著它與一般的函數(shù)指針類(lèi)型并無(wú)二致。請(qǐng)仔細(xì)觀察,HealthCalcFunc所表示的自定義類(lèi)型:

            std::tr1::function<int(const GameCharacter&)>

            在這里,我對(duì)此tr1::function實(shí)例“目標(biāo)簽名”做加粗處理。這里目標(biāo)簽名為“一個(gè)包含const GameCharacter&參數(shù)并返回int類(lèi)型的函數(shù)”。此tr1::function類(lèi)型(也就是HealthCalcFunc類(lèi)型)的對(duì)象可以保存兼容此目標(biāo)簽名的任意可調(diào)用實(shí)體。“兼容”意味著實(shí)體中必須包含(或可隱式轉(zhuǎn)換為)const GameCharacter&類(lèi)型的參數(shù),并且此實(shí)體必須返回一個(gè)(或可隱式轉(zhuǎn)換為)int類(lèi)型的返回值。

            與上文中我們剛剛看到過(guò)的設(shè)計(jì)方案(GameCharacter保存一個(gè)函數(shù)指針)相比,本方案并沒(méi)有顯著的改動(dòng)。二者僅有的差別就是后一版本中GameCharacter可以保存tr1::function對(duì)象——指向函數(shù)的一般化指針。這一改變實(shí)際上是十分微不足道的,甚至有些跑題,但是,它顯著提高了客戶(hù)在程序中修改角色的生命值計(jì)算函數(shù)的靈活性,這一點(diǎn)還是沒(méi)有偏離議題的:

            short calcHealth(const GameCharacter&);      // 生命值計(jì)算函數(shù)

                                                          // 注意:返回值類(lèi)型非int

             

            struct HealthCalculator {                    // 生命值計(jì)算類(lèi)

              int operator()(const GameCharacter&) const // 函數(shù)對(duì)象

              { ... }

            };

             

            class GameLevel {

            public:

              float health(const GameCharacter&) const;  // 生命值計(jì)算成員函數(shù)

              ...                                         // 注意:返回類(lèi)型非int

            };

             

            class EvilBadGuy: public GameCharacter {     // 同上

              ...

            };

            class EyeCandyCharacter: public GameCharacter {

              ...                                         // 另一個(gè)角色類(lèi)型;

            };                                            // 假設(shè)與EvilBadGuy

                                                          // 有同樣的構(gòu)造函數(shù)

             

            EvilBadGuy ebg1(calcHealth);                 // 使用函數(shù)的角色

             

            EyeCandyCharacter ecc1(HealthCalculator());  // 使用函數(shù)對(duì)象的角色

             

            GameLevel currentLevel;

            ...

            EvilBadGuy ebg2( std::tr1::bind(&GameLevel::health, currentLevel, _1) );

                                                          // 使用成員函數(shù)的角色

                                                          // 詳情見(jiàn)下文

            我個(gè)人認(rèn)為,tr1::function可以把你帶入一個(gè)全新的美妙境界,它讓我“熱血沸騰”。如果你的熱血暫時(shí)沒(méi)有那么“沸騰”,那可能是因?yàn)槟氵€糾結(jié)于ebg2的定義,你還不了解tr1::bind的工作機(jī)理。請(qǐng)?jiān)试S我對(duì)它做出介紹。

            我要說(shuō)的是,要計(jì)算ebg2的生命值,需要使用GameLevel類(lèi)中的health成員函數(shù)。現(xiàn)在,我們所聲明的GameLevel::health函數(shù)表面上看包含一個(gè)參數(shù)(一個(gè)指向GameCharacter對(duì)象的引用),但是它實(shí)際上包含兩個(gè)參數(shù),這是因?yàn)樗€包含一個(gè)隱式的GameLevel參數(shù)(this指針?biāo)笇?duì)象)。然而,GameCharacter類(lèi)的生命值計(jì)算函數(shù)僅包含一個(gè)參數(shù)(需要計(jì)算生命值的GameCharacter對(duì)象)。如果我們使用GameLevel::health函數(shù)來(lái)計(jì)算ebg2的生命值的話,為了“適應(yīng)”它,我們這里只能使用一個(gè)參數(shù)(GameCharacter),而不能使用兩個(gè)(GameCharacter, GameLevel),這種情況有些蹩腳。本例中,對(duì)于ebg2的生命值計(jì)算,我們希望始終選用currentLevel作為GameLevel對(duì)象,我們可以將currentLevel作為GemeLevel對(duì)象將兩者“綁定”(bind)起來(lái),這樣,我們?cè)诿看问褂?span style="font-family:"Courier New";">GameLevel::health函數(shù)來(lái)計(jì)算ebg2的生命值時(shí),都可以使用綁定好的二者。這就是tr1::bind調(diào)用所做的:他告訴ebg2的生命值計(jì)算函數(shù),應(yīng)該一直指定currentLevel作為GameLevel對(duì)象。

            上文中我略去了大量細(xì)節(jié)內(nèi)容,比如說(shuō),為什么“_1”意味著“在為ebg2調(diào)用GameLevel::health時(shí)選用currentLevel作為GameLevel對(duì)象”。這些細(xì)節(jié)并不太難理解,而且它們與本節(jié)所講的基本點(diǎn)并無(wú)關(guān)系,通過(guò)使用tr1::function來(lái)代替函數(shù)指針,在計(jì)算角色的生命值時(shí),我們可以讓客戶(hù)使用任意可調(diào)用實(shí)體。這是再酷不過(guò)的事情了!

            “經(jīng)典”策略模式

            如果相對(duì)于用C++來(lái)耍酷,你更加深諳設(shè)計(jì)模式之道,那么更加傳統(tǒng)的策略模式是這樣實(shí)現(xiàn)的:將生命值計(jì)算函數(shù)聲明為虛函數(shù)。并在此基礎(chǔ)上創(chuàng)建一個(gè)獨(dú)立的生命值計(jì)算層次結(jié)構(gòu)。以下的UML圖體現(xiàn)了這一設(shè)計(jì)方案:


            如果UML符號(hào)還是讓你一頭霧水的話,我就簡(jiǎn)單點(diǎn)兒說(shuō)吧:GameCharacter是一個(gè)繼承層次結(jié)構(gòu)中的基類(lèi),EvilBadGuyEyeCandyCharacter是派生類(lèi);HealthCalcFunc是另一個(gè)繼承層次結(jié)構(gòu)的基類(lèi),其下包含SlowHealthLoserFastHealthLoser等派生類(lèi)。GameCharacter類(lèi)型的每一個(gè)對(duì)象都有一個(gè)指針,這一指針指向?qū)?yīng)的HealthCalcFunc層次結(jié)構(gòu)中的對(duì)象。

            以下是對(duì)應(yīng)的代碼框架:

            class GameCharacter;               // 前置聲明

             

            class HealthCalcFunc {

            public:

              ...

              virtual int calc(const GameCharacter& gc) const

              { ... }

              ...

            };

             

            HealthCalcFunc defaultHealthCalc;

             

            class GameCharacter {

            public:

              explicit GameCharacter(HealthCalcFunc *phcf = &defaultHealthCalc)

              : pHealthCalc(phcf)

              {}

             

              int healthValue() const

              { return pHealthCalc->calc(*this);}

              ...

            private:

              HealthCalcFunc *pHealthCalc;

            };

            以上的方案可以使熟悉“標(biāo)準(zhǔn)”策略模式實(shí)現(xiàn)方式的朋友很快的識(shí)別出來(lái),同時(shí),此方案還可以通過(guò)為HealthCalcFunc層次結(jié)構(gòu)中添加一個(gè)派生類(lèi)的方式,使對(duì)現(xiàn)有的生命值算法作出改進(jìn)成為可能。

            小節(jié)

            本條目中最基本的建議是:在你為當(dāng)前的問(wèn)題設(shè)計(jì)解決方案時(shí),不妨考慮一下虛函數(shù)以外的其他替代方案。以下是對(duì)上述替代方案的概括:

            使用“非虛擬接口”慣用法(NVI慣用法),它是模板方法設(shè)計(jì)模式的一種形式,它將公有非虛成員函數(shù)包裝成為更低訪問(wèn)權(quán)限的虛函數(shù)。

            使用函數(shù)指針數(shù)據(jù)成員代替虛函數(shù)。策略設(shè)計(jì)模式一個(gè)簡(jiǎn)裝的表現(xiàn)形式。

            使用tr1::function數(shù)據(jù)成員代替虛函數(shù)。可以使用任意可調(diào)用實(shí)體,只要實(shí)體特征與所期待的相兼容即可。同樣是策略設(shè)計(jì)模式的一種形式。

            使用另一個(gè)層次結(jié)構(gòu)中的虛函數(shù)代替同一層次結(jié)構(gòu)內(nèi)的虛函數(shù)。這是策略設(shè)計(jì)模式的傳統(tǒng)實(shí)現(xiàn)形式。

            以上并不是虛函數(shù)替代方案的完整列表,但是這足以說(shuō)服你虛函數(shù)存在替代方案的。此外,這些方案的比較優(yōu)勢(shì)和劣勢(shì),清楚的告訴你在應(yīng)用的過(guò)程中應(yīng)該對(duì)它們加以考慮。

            為了避免陷入面向?qū)ο笤O(shè)計(jì)的思維定勢(shì),我們不妨不失時(shí)機(jī)的另辟蹊徑,所謂“條條大路通羅馬”。花些時(shí)間來(lái)研究這些替代方案還是頗有裨益的。

            時(shí)刻牢記

            虛函數(shù)的替代方案包括:NVI慣用法和策略模式的不同實(shí)現(xiàn)方式。NVI慣用法是模板方法設(shè)計(jì)模式的一個(gè)實(shí)例。

            將成員函數(shù)的功能挪至類(lèi)外部存在著以下缺點(diǎn):非成員函數(shù)對(duì)類(lèi)的非公有成員沒(méi)有訪問(wèn)權(quán)限。

            tr1::function對(duì)象就像更一般化的函數(shù)指針。這類(lèi)對(duì)象支持給定特征的任意可調(diào)用實(shí)體。

            posted on 2011-12-25 00:59 ★ROY★ 閱讀(3264) 評(píng)論(2)  編輯 收藏 引用 所屬分類(lèi): Effective C++

            評(píng)論

            # re: 【讀書(shū)筆記】[Effective C++中文版第3版][第35條]為虛函數(shù)尋求替代方案   回復(fù)  更多評(píng)論   

            不錯(cuò),剛一看還以為是樓主寫(xiě)的,那不得了的。
            好多年沒(méi)看過(guò)effective c++了,想不到這第3版確實(shí)改進(jìn)不少。
            2011-12-25 12:01 | 春秋十二月

            # re: 【讀書(shū)筆記】[Effective C++中文版第3版][第35條]為虛函數(shù)尋求替代方案   回復(fù)  更多評(píng)論   

            用非虛函數(shù)來(lái)包裝虛函數(shù)這個(gè)做法雖然早就聽(tīng)過(guò), 但是我一直沒(méi)明白什么場(chǎng)合需要用. 到使用虛函數(shù)包裝非虛函數(shù)的"代理"技術(shù), 倒是時(shí)不常的能用到.
            2011-12-25 19:08 | 欲三更
            久久久久国产一级毛片高清板| 久久综合狠狠综合久久| 久久99热精品| 久久大香萑太香蕉av| 国产成人久久精品二区三区| 亚洲第一极品精品无码久久| 久久99精品久久久久久9蜜桃| 久久精品成人欧美大片| 国产免费久久久久久无码| 久久久久亚洲av毛片大| 久久香蕉一级毛片| 久久综合国产乱子伦精品免费| 久久亚洲欧美日本精品| 无码任你躁久久久久久老妇| 久久91这里精品国产2020| 亚洲国产精品无码久久SM| 国产一区二区精品久久岳| 国产精品一区二区久久不卡| 青草国产精品久久久久久| 久久99精品久久久久久9蜜桃| 久久精品无码专区免费青青| 狠狠色婷婷久久一区二区三区| 久久精品国产99国产电影网| 日产精品久久久一区二区| 亚洲国产天堂久久久久久| 一个色综合久久| 人妻无码αv中文字幕久久琪琪布 人妻无码久久一区二区三区免费 人妻无码中文久久久久专区 | 亚洲精品乱码久久久久久蜜桃| 久久99精品久久久久久不卡| 国产成人无码久久久精品一| 亚洲欧美伊人久久综合一区二区| 欧美伊人久久大香线蕉综合69| 国产三级观看久久| 国内精品久久久久国产盗摄| 成人精品一区二区久久久| 蜜桃麻豆www久久| 91精品国产91久久久久久青草 | 婷婷综合久久中文字幕| 国产午夜精品久久久久免费视| 亚洲中文字幕无码久久综合网| 久久久一本精品99久久精品88|