• <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ū)筆記 條目15] 要為資源管理類(lèi)提供對(duì)原始資源的訪問(wèn)權(quán)

            資源管理類(lèi)的特征是振奮人心的。它構(gòu)筑起一道可靠的屏障,可有效地防止資源泄漏。能否預(yù)防資源泄漏是“系統(tǒng)的設(shè)計(jì)方案是否優(yōu)異”的一個(gè)基本評(píng)判標(biāo)準(zhǔn)。在完美的世界里,你應(yīng)該依靠資源管理類(lèi)來(lái)完成所有的與資源交互的工作,而永遠(yuǎn)不要直接訪問(wèn)原始資源。然而世界并不是完美的。由于許多API會(huì)直接引用資源,因此除非你發(fā)誓不使用這樣的API(這樣做顯得太不實(shí)際了),否則,你必須繞過(guò)資源管理類(lèi),然后在需要的時(shí)候及時(shí)手工處理原始資源。

            舉例說(shuō),條目13中引入了下面的做法:使用諸如auto_ptr或者tr1::shared_ptr這樣的智能指針來(lái)保存諸如createInvestment的工廠函數(shù)的返回值:

            std::tr1::shared_ptr<Investment> pInv(createInvestment());

                                                   // 來(lái)自條目13

            假設(shè),當(dāng)你使用Investment對(duì)象時(shí),你需要一個(gè)這樣的函數(shù):

            int daysHeld(const Investment *pi);   // 返回持有投資的天數(shù)

            你可能希望這樣來(lái)調(diào)用它:

            int days = daysHeld(pInv);            // 錯(cuò)!

            但是這段代碼無(wú)法通過(guò)編譯:因?yàn)?span style="font-family:"Courier New";">daysHeld需要一個(gè)原始的Investment*指針,但是你傳遞給它的對(duì)象的類(lèi)型卻是tr1::shared_ptr<Investment>。

            你需要一個(gè)渠道來(lái)將一個(gè)RAII類(lèi)的對(duì)象(在上面的示例中是tr1::shared_ptr)轉(zhuǎn)變?yōu)樗脑假Y源(比如說(shuō),原始的Investment*)。這里實(shí)現(xiàn)這一轉(zhuǎn)變有兩個(gè)一般的方法:顯式轉(zhuǎn)換和隱式轉(zhuǎn)換。

            tr1::shared_ptrauto_ptr都提供了一個(gè)get成員函數(shù)來(lái)進(jìn)行顯式轉(zhuǎn)換,也就是說(shuō),返回一個(gè)智能指針對(duì)象中的原始指針(的副本):

            int days = daysHeld(pInv.get());          // 工作正常,
                                                                     // pInv中的原始指針傳遞給daysHeld

            似乎所有的智能指針類(lèi),包括tr1::shared_ptrauto_ptr等等,都會(huì)重載指針解析運(yùn)算符(operator->operator*),這便使得你可以對(duì)原始指針進(jìn)行隱式轉(zhuǎn)換:

            class Investment {                 // 投資類(lèi)型的層次結(jié)構(gòu)中
                                                               // 最為根基的類(lèi)

            public:

              bool isTaxFree() const;

              ...

            };

             

            Investment* createInvestment();   // 工廠函數(shù)

             

            std::tr1::shared_ptr<Investment> pi1(createInvestment());
                                                              // 使用tr1::shared_ptr管理資源

            bool taxable1 = !(pi1->isTaxFree());
                                                              // 通過(guò)operator->訪問(wèn)資源

            ...

             

            std::auto_ptr<Investment> pi2(createInvestment());
                                                              // 使用auto_ptr管理資源

            bool taxable2 = !((*pi2).isTaxFree());
                                                              // 通過(guò)operator*訪問(wèn)資源

            ...

            由于某些時(shí)刻你需要獲取一個(gè)RAII對(duì)象中的原始資源,所以一些RAII類(lèi)的設(shè)計(jì)者使用了一個(gè)小手段來(lái)使系統(tǒng)正常運(yùn)行,那就是:提供一個(gè)隱式轉(zhuǎn)換函數(shù)。舉例說(shuō),以下是一個(gè)C版本API中提供的處理字體的RAII類(lèi):

            FontHandle getFont();              // 來(lái)自一個(gè)C版本API
                                                               // 省略參數(shù)表以簡(jiǎn)化代碼

             

            void releaseFont(FontHandle fh);     // 來(lái)自同一個(gè)C版本API

             

            class Font {                       // RAII類(lèi)

            public:
              explicit Font(FontHandle fh)     // 通過(guò)傳值獲取資源
               : f(fh)                          // 因?yàn)樵?/span>C版本API這樣做

              {}

              ~Font() { releaseFont(f); }      // 釋放資源 

            private:
              FontHandle f;                    // 原始的字體資源

            };

            假設(shè)這里有一個(gè)大型的與字體相關(guān)的C版本API通過(guò)FontHandle解決所有問(wèn)題,那么把Font對(duì)象轉(zhuǎn)換為FontHandle的操作將十分頻繁。Font類(lèi)可以提供一個(gè)顯式轉(zhuǎn)換函數(shù),比如get

            class Font {

            public:

              ...

              FontHandle get() const { return f; }
                                                               // 進(jìn)行顯式轉(zhuǎn)換的函數(shù)

              ...

            };

            遺憾的是,這樣做使得客戶在每次與這一API通信時(shí)都要調(diào)用一次get

            void changeFontSize(FontHandle f, int newSize);
                                                               // 來(lái)自該C語(yǔ)言API

            Font f(getFont());
            int newFontSize;

            ...

            changeFontSize(f.get(), newFontSize);
                                                               // 顯式轉(zhuǎn)換:從FontFontHandle

            一些程序員可能會(huì)發(fā)現(xiàn),由于使用這個(gè)類(lèi)要求我們始終提供上述示例中的那種顯式轉(zhuǎn)換,這一點(diǎn)很糟糕,足夠讓他們拒絕使用這個(gè)類(lèi)了。同時(shí)這一設(shè)計(jì)又增加了字體資源泄漏的可能性,這與Font類(lèi)的設(shè)計(jì)初衷是完全相悖的。

            有一個(gè)替代方案,讓Font提供一個(gè)可隱式轉(zhuǎn)換為Fonthandle的函數(shù):

            class Font {

            public:

              ...

              operator FontHandle() const { return f; }
                                                                // 進(jìn)行隱式轉(zhuǎn)換的函數(shù)

             

              ...

            };

            這使得調(diào)用這一C版本API的工作變得簡(jiǎn)潔而且自然:

            Font f(getFont());

            int newFontSize;

            ...

             

            changeFontSize(f, newFontSize);   // 隱式轉(zhuǎn)換:從FontFontHandle

             

            隱式轉(zhuǎn)換會(huì)帶來(lái)一定的負(fù)面效應(yīng):它會(huì)增加出錯(cuò)的可能。比如說(shuō),一個(gè)客戶在一個(gè)需要Font的地方意外地創(chuàng)建了一個(gè)FontHandle

            Font f1(getFont());

            ...

            FontHandle f2 = f1;                // 啊哦!本想復(fù)制一個(gè)Font對(duì)象,
                                                               // 但是卻卻將f1隱式轉(zhuǎn)換為其原始的
                                                               // FontHandle,然后復(fù)制它

            現(xiàn)在程序中有一個(gè)FontHandle資源正在由Font對(duì)象f1來(lái)管理,但是仍然可以通過(guò)f2直接訪問(wèn)FontHandle資源。這是很糟糕的。比如說(shuō),當(dāng)f1被銷(xiāo)毀時(shí),字體就會(huì)被釋放,f2將無(wú)法被銷(xiāo)毀。

            是為RAII類(lèi)提供顯式轉(zhuǎn)換為潛在資源的方法,還是允許隱式轉(zhuǎn)換,上面兩個(gè)問(wèn)題的答案取決于RAII類(lèi)設(shè)計(jì)用于完成的具體任務(wù),及其被使用的具體環(huán)境。最好的設(shè)計(jì)方案應(yīng)該遵循條目18的建議,讓接口更容易被正確使用,而不易被誤用。通常情況下,定義一個(gè)類(lèi)似于get的顯式轉(zhuǎn)換函數(shù)是一個(gè)較好的途徑,應(yīng)為它可以使非故意類(lèi)型轉(zhuǎn)換的可能性降至最低。然而,一些時(shí)候使用隱式類(lèi)型轉(zhuǎn)換顯得更加自然,人們更趨向于使用它。

            你可能已經(jīng)發(fā)現(xiàn),讓一個(gè)函數(shù)返回一個(gè)RAII類(lèi)內(nèi)部的原始資源是違背封裝性原則的。的確是這樣,乍看上去這簡(jiǎn)直就是設(shè)計(jì)災(zāi)難,但是它實(shí)際上并沒(méi)有那么糟糕。RAII類(lèi)并不是用來(lái)封裝什么的,它們是用來(lái)確保一些特別的操作能夠得以執(zhí)行的,那就是資源釋放。如果需要,資源封裝工作可以放在這一主要功能的最頂端,但是這并不是必需的。另外,一些RAII類(lèi)結(jié)合了實(shí)現(xiàn)封裝的嚴(yán)格性和原始資源封裝的寬松性。比如tr1::shared_ptr對(duì)其引用計(jì)數(shù)機(jī)制進(jìn)行了整體封裝,但是它仍然為其所包含的原始指針提供了方便的訪問(wèn)方法。就像其它設(shè)計(jì)優(yōu)秀的類(lèi)一樣,它隱藏了客戶不需要關(guān)心的內(nèi)容,但是它使得客戶的確需要訪問(wèn)的部分對(duì)其可見(jiàn)。

            時(shí)刻牢記

            API通常需要訪問(wèn)原始資源,所以每個(gè)RAII類(lèi)都應(yīng)該提供一個(gè)途徑來(lái)獲取它所管理的資源。

            訪問(wèn)可以通過(guò)顯式轉(zhuǎn)換或隱式轉(zhuǎn)換來(lái)實(shí)現(xiàn)。一般情況下,顯式轉(zhuǎn)換更安全,而隱式轉(zhuǎn)換對(duì)于客戶來(lái)說(shuō)更方便。

            posted on 2007-05-13 20:54 ★ROY★ 閱讀(833) 評(píng)論(0)  編輯 收藏 引用 所屬分類(lèi): Effective C++

            久久激情五月丁香伊人| 久久丫精品国产亚洲av不卡| 久久亚洲精品视频| 国产三级精品久久| 午夜精品久久久久久久久| 777米奇久久最新地址| 久久久久亚洲AV无码去区首| 亚洲一级Av无码毛片久久精品| 久久精品一本到99热免费| 久久免费精品一区二区| 久久精品久久久久观看99水蜜桃| 国产精品久久久久久久久鸭| 久久国产欧美日韩精品免费| 精品国产91久久久久久久| 久久久久亚洲AV无码专区首JN| 久久精品一区二区三区不卡| 中文国产成人精品久久亚洲精品AⅤ无码精品 | 久久夜色精品国产网站| 综合久久精品色| 国产精品熟女福利久久AV| 久久久久久九九99精品| 99蜜桃臀久久久欧美精品网站| 99久久精品无码一区二区毛片 | 国产精品乱码久久久久久软件| 精品精品国产自在久久高清| 久久婷婷五月综合色高清| 亚洲伊人久久综合影院| 久久久久99精品成人片牛牛影视| 国产精品欧美久久久天天影视| 99久久婷婷国产综合亚洲| 精品国产乱码久久久久软件| 欧美国产精品久久高清| 国产激情久久久久影院| 久久黄视频| 久久久久99这里有精品10 | 精品久久久久久久久久久久久久久| 久久99热这里只有精品国产| 欧美精品国产综合久久| 91麻豆国产精品91久久久| 久久精品国产乱子伦| 久久亚洲AV成人无码|