C++箴言:將new出來(lái)的對(duì)象存入智能指針

字號(hào):

假設(shè)我們有一個(gè)函數(shù)取得我們的處理優(yōu)先級(jí),而第二個(gè)函數(shù)根據(jù)優(yōu)先級(jí)針對(duì)動(dòng)態(tài)分配的 Widget 做一些處理:
    int priority();
    void processWidget(std::tr1::shared_ptr pw, int priority);
    不要忘記使用對(duì)象管理資源的至理名言,processWidget 為處理動(dòng)態(tài)分配的 Widget 使用了一個(gè)智能指針(在此,是一個(gè) tr1::shared_ptr)。
    現(xiàn)在考慮一個(gè)對(duì) processWidget 的調(diào)用:
    processWidget(new Widget, priority());
    且慢,別想這樣調(diào)用。它不能編譯。tr1::shared_ptr 的構(gòu)造函數(shù)取得一個(gè)*指針(raw pointer)應(yīng)該是顯式的,所以不能從一個(gè)由 "new Widget" 返回的*指針隱式轉(zhuǎn)型到 processWidget 所需要的 tr1::shared_ptr。下面的代碼,無(wú)論如何,是可以編譯的:
    processWidget(std::tr1::shared_ptr(new Widget), priority());
    令人驚訝的是,盡管我們?cè)谶@里各處都使用了對(duì)象管理資源,這個(gè)調(diào)用還是可能泄漏資源。下面就來(lái)說明這是如何發(fā)生的。
    在編譯器能生成一個(gè)對(duì) processWidget 的調(diào)用之前,它們必須傳遞實(shí)際參數(shù)來(lái)計(jì)算形式參數(shù)的值。第二個(gè)實(shí)際參數(shù)不過是對(duì)函數(shù) priority 的調(diào)用,但是第一個(gè)實(shí)際參數(shù)("std::tr1::shared_ptr(new Widget)"),由兩部分組成
    ·表達(dá)式 "new Widget" 的執(zhí)行。
    ·一個(gè)對(duì) tr1::shared_ptr 的構(gòu)造函數(shù)的調(diào)用。
    在 processWidget 能被調(diào)用之前,編譯器必須為這三件事情生成代碼:
    ·調(diào)用 priority。
    ·執(zhí)行 "new Widget"。
    ·調(diào)用 tr1::shared_ptr 的構(gòu)造函數(shù)。
    C++ 編譯器允許在一個(gè)相當(dāng)大的范圍內(nèi)決定這三件事被完成的順序。(這里與 Java 和 C# 等語(yǔ)言的處理方式不同,那些語(yǔ)言里函數(shù)參數(shù)總是按照一個(gè)精確的順序被計(jì)算。)"new Widget" 表達(dá)式一定在 tr1::shared_ptr 的構(gòu)造函數(shù)能被調(diào)用之前執(zhí)行,因?yàn)檫@個(gè)表達(dá)式的結(jié)果要作為一個(gè)參數(shù)傳遞給 tr1::shared_ptr 的構(gòu)造函數(shù),但是 priority 的調(diào)用可以被第一個(gè),第二個(gè)或第三個(gè)執(zhí)行。如果編譯器選擇第二個(gè)執(zhí)行它(大概這樣能使它們生成更有效率的代碼),我們最終得到這樣一個(gè)操作順序:
    ·執(zhí)行 "new Widget"。
    ·調(diào)用 priority。
    ·調(diào)用 tr1::shared_ptr 的構(gòu)造函數(shù)。
    但是請(qǐng)考慮,如果對(duì) priority 的調(diào)用引發(fā)一個(gè)異常將發(fā)生什么。在這種情況下,從 "new Widget" 返回的指針被丟失,因?yàn)樗鼪]有被存入我們期望能阻止資源泄漏的 tr1::shared_ptr。由于一個(gè)異常可能插入資源創(chuàng)建的時(shí)間和將資源交給一個(gè)資源管理對(duì)象的時(shí)間之間,所以調(diào)用 processWidget 可能會(huì)發(fā)生一次泄漏。 避免類似問題的方法很簡(jiǎn)單:用一個(gè)單獨(dú)的語(yǔ)句創(chuàng)建 Widget 并將它存入一個(gè)智能指針,然后將這個(gè)智能指針傳遞給 processWidget:
    std::tr1::shared_ptr pw(new Widget); // store newed object
    // in a smart pointer in a
    // standalone statement
    processWidget(pw, priority()); // this call won’t leak
    這樣做是因?yàn)榫幾g器在不同的語(yǔ)句之間重新安排操作順序的活動(dòng)余地比在一個(gè)語(yǔ)句之內(nèi)要小得多。"new Widget" 表達(dá)式和 tr1::shared_ptr 的構(gòu)造函數(shù)的調(diào)用與 priority 的調(diào)用在不同的語(yǔ)句中,所以編譯器不會(huì)允許 priority 的調(diào)用插入它們中間。
    Things to Remember
    ·在一個(gè)獨(dú)立的語(yǔ)句中將 new 出來(lái)的對(duì)象存入智能指針。如果疏忽了這一點(diǎn),當(dāng)異常發(fā)生時(shí),可能引起微妙的資源泄漏。