C++箴言:在資源管理類中準備訪問裸資源

字號:

資源管理類真是太棒了。他們是你防御資源泄漏的防波堤,沒有這樣的泄漏是設(shè)計良好的系統(tǒng)的基本特征。在一個完美的世界中,你可以在所有與資源的交互中依賴這樣的類,從來不需要因為直接訪問*資源(raw resources)而玷污你的手。但是這個世界并不完美,很多 API 直接涉及資源,所以除非你計劃堅決放棄使用這樣的 API(這種事很少會成為實際),否則,你就要經(jīng)常繞過資源管理類而直接處理*資源(raw resources)。
    例如,以前介紹的使用類似 auto_ptr 或 tr1::shared_ptr 這樣的智能指針來持有調(diào)用類似 createInvestment 這樣的 factory 函數(shù)的結(jié)果: std::tr1::shared_ptr pInv(createInvestment());
    假設(shè)你打算使用的一個與 Investment 對象一起工作的函數(shù)是這樣的:
    int daysHeld(const Investment *pi); // return number of days
    // investment has been held
    你打算像這樣調(diào)用它,
    int days = daysHeld(pInv); // error!
    但是這代碼不能編譯:daysHeld 要求一個*的 Investment* 指針,但是你傳給它一個類型為 tr1::shared_ptr 的對象。
    你需要一個將 RAII 類(當前情況下是 tr1::shared_ptr)的對象轉(zhuǎn)化為它所包含的*資源(例如,底層的 Investment*)的方法。有兩個常規(guī)方法來做這件事。顯式轉(zhuǎn)換和隱式轉(zhuǎn)換。
    tr1::shared_ptr 和 auto_ptr 都提供一個 get 成員函數(shù)進行顯示轉(zhuǎn)換,也就是說,返回一個智能指針對象內(nèi)部的*指針(raw pointer)(或它的一個副本):
    int days = daysHeld(pInv.get()); // fine, passes the raw pointer
    // in pInv to daysHeld
    就像實際上的所有智能指針類一樣,tr1::shared_ptr 和 auto_ptr 也都重載了指針解引用操作符(pointer dereferencing operators)(operator-> 和 operator*),而這樣就允許隱式轉(zhuǎn)換到底層的*指針(raw pointers):
    class Investment { // root class for a hierarchy
    public: // of investment types
    bool isTaxFree() const;
    ...
    };
    Investment* createInvestment(); // factory function
    std::tr1::shared_ptr // have tr1::shared_ptr
    pi1(createInvestment()); // manage a resource
    bool taxable1 = !(pi1->isTaxFree()); // Access resource
    // via operator->
    ...
    std::auto_ptr pi2(createInvestment()); // have auto_ptr
    // manage a
    // resource
    bool taxable2 = !((*pi2).isTaxFree()); // access resource
    // via operator*
    ...
    因為有些時候有必要取得 RAII 對象內(nèi)部的*資源,所以一些 RAII 類的設(shè)計者就通過提供一個隱式轉(zhuǎn)換函數(shù)來給剎車抹油。例如,考慮以下這個 RAII 類,它要為 C++ API 提供原始狀態(tài)的字體資源:
    FontHandle getFont(); // from C API-params omitted
    // for simplicity
    void releaseFont(FontHandle fh); // from the same C API
    class Font { // RAII class
    public:
    explicit Font(FontHandle fh) // acquire resource;
    : f(fh) // use pass-by-value, because the
    {} // C API does
    ~Font() { releaseFont(f); } // release resource
    private:
    FontHandle f; // the raw font resource
    };