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

            Shuffy

            不斷的學(xué)習(xí),不斷的思考,才能不斷的進(jìn)步.Let's do better together!
            posts - 102, comments - 43, trackbacks - 0, articles - 19
            [轉(zhuǎn)]http://m.shnenglu.com/tiandejian/archive/2007/05/13/cp_15.html

            第15條:     要為資源管理類提供對(duì)原始資源的訪問權(quán)

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

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

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

                                                   // 來自第 13

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

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

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

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

            但是這段代碼無法通過編譯:因?yàn)?/span> daysHeld 需要一個(gè)原始的 Investment* 指針,但是你傳遞給它的對(duì)象的類型卻是 tr1::shared_ptr<Investment>

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

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

            int days = daysHeld(pInv.get());       // 工作正常,將 pInv 中的

                                                   // 裸指針傳遞給 daysHeld

            似乎所有的智能指針類,包括 tr1::shared_ptr auto_ptr 等等,都會(huì)重載指針解析運(yùn)算符( operator-> operator* ),這便使得對(duì)原始裸指針進(jìn)行隱式轉(zhuǎn)換成為現(xiàn)實(shí):

            class Investment {                 // 投資類型的層次結(jié)構(gòu)中

                                               // 最為根基的部分

            public:

             bool isTaxFree() const;

             ...

            };

             

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

             

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

                                               // 使用 tr1::shared_ptr

                                               // 管理資源

            bool taxable1 = !(pi1->isTaxFree());

                                               // 通過 operator-> 訪問資源

            ...

             

            std::auto_ptr<Investment> pi2(createInvestment());

                                               // 使用 auto_ptr 管理資源

            bool taxable2 = !((*pi2).isTaxFree());

                                               // 通過 operator* 訪問資源

            ...

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

            FontHandle getFont();              // 來自一個(gè) C 語言 API

                                               // 省略參數(shù)表以簡化代碼

             

            void releaseFont(FontHandle fh);   // 來自同一個(gè) C 語言 API

             

            class Font {                       // RAII

            public:

             explicit Font(FontHandle fh)     // 通過傳值獲取資源

             : f(fh)                          // 因?yàn)檫@一 C 語言 API 這樣做

             {}

             

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

             

            private:

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

            };

            假設(shè)這里有一個(gè)大型的相關(guān)的 C 語言 API 僅僅通過 FontHandle 解決字體問題,那么將存在十分頻繁的把 Font 對(duì)象轉(zhuǎn)換為 FontHandle 的操作。 Font 類可以提供一個(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);

                                               // 來自一個(gè) C 語言 API

            Font f(getFont());

            int newFontSize;

            ...

             

            changeFontSize(f.get(), newFontSize);

                                               // 顯式轉(zhuǎn)換:從 Font FontHandle

            由于需要顯式請(qǐng)求這樣的轉(zhuǎn)換,這樣做顯得得不償失,一些程序員也許會(huì)拒絕使用這個(gè)類。然而這又增加了字體資源泄漏的可能性,這與 Font 類的設(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 的工作變得簡潔而且自然:

            Font f(getFont());

            int newFontSize;

            ...

             

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

             

            隱式轉(zhuǎn)換會(huì)帶來一定的負(fù)面效應(yīng):它會(huì)增加出錯(cuò)的可能。比如說,一個(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 來管理,但是仍然可以通過 f2 直接訪問 FontHandle 資源。這是很糟糕的。比如說,當(dāng) f1 被銷毀時(shí),字體就會(huì)被釋放, f2 也一樣。

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

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

            牢記在心

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

            訪問可以通過顯式轉(zhuǎn)換或隱式轉(zhuǎn)換來實(shí)現(xiàn)。一般情況下,顯式轉(zhuǎn)換更安全,但是隱式轉(zhuǎn)換對(duì)于客戶端程序員來說使用更方便。

            国产成人久久激情91| 亚洲婷婷国产精品电影人久久| 久久综合久久鬼色| 日韩人妻无码一区二区三区久久99 | 国产精品久久影院| 亚洲午夜精品久久久久久人妖| 国产A级毛片久久久精品毛片| 久久国产成人午夜AV影院| 中文字幕久久波多野结衣av| 久久er99热精品一区二区| 久久影院亚洲一区| 精品久久无码中文字幕| 国产综合精品久久亚洲| 日韩精品久久久肉伦网站| 99国内精品久久久久久久| 2021国产精品久久精品| 99精品久久久久久久婷婷| 久久青青草原精品国产| 精品乱码久久久久久夜夜嗨| 久久久亚洲欧洲日产国码二区| 久久综合亚洲色HEZYO国产| 精品久久久无码人妻中文字幕豆芽| 久久久久久久久久久免费精品| 人妻久久久一区二区三区| 一本大道久久香蕉成人网| 久久97精品久久久久久久不卡| 亚洲精品午夜国产VA久久成人| 久久99精品久久久久久齐齐| 久久九九青青国产精品| 久久精品无码一区二区无码| 日韩人妻无码精品久久免费一| 一本久久精品一区二区| 天天综合久久一二三区| 精品99久久aaa一级毛片| 久久这里只有精品首页| 久久久精品午夜免费不卡| 久久精品国产亚洲77777| 久久精品www人人爽人人| 少妇内射兰兰久久| 国产精品久久久久…| 精品一区二区久久|