讀書筆記 effective c++ Item 14 對資源管理類的拷貝行為要謹慎。本站提示廣大學習愛好者:(讀書筆記 effective c++ Item 14 對資源管理類的拷貝行為要謹慎)文章只能為提供參考,不一定能成為您想要的結果。以下是讀書筆記 effective c++ Item 14 對資源管理類的拷貝行為要謹慎正文
Item 13中介紹了 “資源獲取之時也是初始化之時(RAII)”的概念,這個概念被當作資源管理類的“脊柱“,也描述了auto_ptr和tr1::shared_ptr是如何用堆資源來表現這個概念的。然而並不是所有資源都是在堆上創建的,對於這種資源,像auto_ptr和tr1::shared_ptr這樣的智能指針就不適合當作資源句柄(handle)來使用了。你會發現你時不時的就會需要創建自己的資源管理類。
舉個例子,假設你正在使用C API來操縱Mutex類型的互斥信號量對象,來為函數提供lock和unlock:
1 void lock(Mutex *pm); // lock mutex pointed to by pm 2 3 void unlock(Mutex *pm); // unlock the mutex
為了確保你不會忘記unlock一個已經加過鎖的Mutex,你需要創建一個類來管理鎖。這樣一個類的基本結構已經由RAII准則表述過了,也就是資源會在執行構造的時候獲取到,在執行析構的時候釋放掉:
1 class Lock { 2 3 public: 4 5 explicit Lock(Mutex *pm) 6 7 : mutexPtr(pm) 8 9 { lock(mutexPtr); } // acquire resource 10 11 ~Lock() { unlock(mutexPtr); } // release resource 12 13 private: 14 15 Mutex *mutexPtr; 16 17 };
客戶端以傳統的RAII方式來使用鎖:
1 Mutex m; // define the mutex you need to use 2 3 ... 4 5 { // create block to define critical section 6 7 Lock ml(&m); // lock the mutex 8 9 ... // perform critical section operations 10 11 } // automatically unlock mutex at end 12 13 // of block
2. 對資源管理類進行拷貝會發生什麼?
這很好,但如果一個鎖對象被拷貝會發生什麼呢?
1 Lock ml1(&m); // lock m 2 3 Lock ml2(ml1); // copy ml1 to ml2 — what should 4 5 // happen here?
上面是一個更加普通的問題,也是每個RAII類的作者必須面對的:當一個RAII對象被拷貝的時候應該發生什麼呢?大多數情況下,你將會從下面的4種可能中選擇一個:
2.1 禁止拷貝1 class Lock: private Uncopyable { // prohibit copying — see 2 3 public: // Item 6 4 5 ... // as before 6 7 };
2.2 一份資源,多次引用——使用tr1::shared_ptr
通常情況下,RAII類可以通過包含一個tr1::shared_ptr數據成員來實現引用計數的拷貝行為。舉個例子,如果Lock想使用引用計數,它可以將mutexPtr的類型從Mutex*改為tr1::shared_ptr<Mutex>。不幸的是,tr1::shared_ptr的默認行為是當引用技術為0的時候會刪除它所指向的資源,這不是我們想要的。當我們實現一個Mutex類時,我們只是想unlock,並不想刪除它們。幸運的是,tr1::shared_ptr允許指定自己的刪除器(”deleter”)---一個函數或者函數對象,引用計數為0的時候會自動調用這個對像。(auto_ptr中不存在這個功能,它總是會刪除指針。)這個刪除器是tr1::shared_ptr構造函數的第二個可選參數,所以代碼會是下面這個樣子:
1 class Lock { 2 3 public: 4 5 explicit Lock(Mutex *pm) // init shared_ptr with the Mutex 6 7 : mutexPtr(pm, unlock) // to point to and the unlock func 8 9 { // as the deleter† 10 11 lock(mutexPtr.get()); // see Item 15 for info on “get” 12 13 } 14 15 private: 16 17 std::tr1::shared_ptr<Mutex> mutexPtr; // use shared_ptr 18 19 }; // instead of raw pointer
注意在這個例子中,Lock類不再聲明析構函數。因為沒有必要了。Item 5 解釋到一個類的析構函數(無論是編譯器生成的還是用戶定義的)會自動調用類中的非靜態數據成員的析構函數。在這個例子中,非靜態數據成員為mutexPtr。但是在mutex的引用計數為0的時候其的析構函數會自動調用tr1::shared_ptr的刪除器—也即是unlock。(人們在看到類的源碼的時候如果有一行注釋來說明你沒有忘記析構,你只是使用了編譯器默認生成的析構函數,他們會很感激的。)
2.3 一份資源,多次拷貝——深拷貝有一些標准string類型的實現中包含了指向堆內存的指針,組成string的字符會保存在這塊內存中。當一個string對象被拷貝的時候,會同時拷貝指針和指針指向的內存。這樣的string展示出來的是深拷貝。
2.4 一份資源,一次引用,轉移所有權——使用auto_ptr拷貝函數可能由編譯器生成,所以除非編譯器生成版本能夠做到你想要的(Item 5解釋了默認版本的行為),否則你需要自己實現它們。一些情況下你可能想支持這些函數的一般版本。這些版本在Item 45進行描述。
3. 總結