C++箴言:謹(jǐn)慎考慮資源管理類的拷貝行為

字號(hào):

在上一篇文章中介紹了作為資源管理類支柱的 Resource Acquisition Is Initialization (RAII) 原則,并描述了 auto_ptr 和 tr1::shared_ptr 在基于堆的資源上運(yùn)用這一原則的表現(xiàn)。并非所有的資源都是基于堆的,然而,對(duì)于這樣的資源,像 auto_ptr 和 tr1::shared_ptr 這樣的智能指針通常就不像 resource handlers(資源管理者)那樣合適。在這種情況下,有時(shí),你可能要根據(jù)你自己的需要去創(chuàng)建你自己的資源管理類。
    例如,假設(shè)你使用 C API 提供的 lock 和 unlock 函數(shù)去操縱 Mutex 類型的互斥體對(duì)象:
    void lock(Mutex *pm); // lock mutex pointed to by pm
    void unlock(Mutex *pm); // unlock the mutex
    為了確保你從不會(huì)忘記解鎖一個(gè)被你加了鎖的 Mutex,你希望創(chuàng)建一個(gè)類來(lái)管理鎖。RAII 原則規(guī)定了這樣一個(gè)類的基本結(jié)構(gòu),通過(guò)構(gòu)造函數(shù)獲取資源并通過(guò)析構(gòu)函數(shù)釋放它:
    class Lock {
    public:
    explicit Lock(Mutex *pm)
    : mutexPtr(pm)
    { lock(mutexPtr); } // acquire resource
    ~Lock() { unlock(mutexPtr); } // release resource
    private:
    Mutex *mutexPtr;
    };
    客戶按照 RAII 風(fēng)格的慣例來(lái)使用 Lock:
    Mutex m; // define the mutex you need to use
    ...
    { // create block to define critical section
    Lock ml(&m); // lock the mutex
    ... // perform critical section operations
    } // automatically unlock mutex at end
    // of block
    這沒(méi)什么問(wèn)題,但是如果一個(gè) Lock 對(duì)象被拷貝應(yīng)該發(fā)生什么?
    Lock ml1(&m); // lock m
    Lock ml2(ml1); // copy ml1 to ml2-what should
    // happen here?
    這是一個(gè)更一般問(wèn)題的特定實(shí)例,每一個(gè) RAII 類的作者都要面臨這樣的問(wèn)題:當(dāng)一個(gè) RAII 對(duì)象被拷貝的時(shí)候應(yīng)該發(fā)生什么?大多數(shù)情況下,你可以從下面各種可能性中挑選一個(gè):
    禁止拷貝。在很多情況下,允許 RAII 被拷貝是沒(méi)有意義的。這對(duì)于像 Lock 這樣類很可能是正確的,因?yàn)橥降幕疽氐摹案北尽焙苌儆惺裁匆饬x。當(dāng)拷貝對(duì)一個(gè) RAII 類沒(méi)有什么意義的時(shí)候,你應(yīng)該禁止它。Item 6 解釋了如何做到這一點(diǎn)。聲明拷貝操作為私有。對(duì)于 Lock,看起來(lái)也許像這樣:
    class Lock: private Uncopyable { // prohibit copying - see
    public: // Item 6
    ... // as before
    };
    對(duì)底層的資源引用計(jì)數(shù)。有時(shí)人們需要的是保持一個(gè)資源直到最后一個(gè)使用它的對(duì)象被銷毀。在這種情況下,拷貝一個(gè) RAII 對(duì)象應(yīng)該增加引用這一資源的對(duì)象的數(shù)目。這也就是使用 tr1::shared_ptr 時(shí)“拷貝”的含意。