您好,登錄后才能下訂單哦!
《Effective C++》 讀書筆記之三 資源管理
準備知識:
所謂資源就是,一旦用了它,將來必須還給系統。最常用的資源是動態分配內存,其他常見的資源有文件描述器、互斥鎖、圖形界面的字形和筆刷、數據庫連接以及網絡sockets。
auto_ptr 是個“類指針對象”,就是所謂的智能指針,其析構函數自動對其所指對象調用delete。auto_ptr位于 #include <memory> 頭文件。由于auto_ptr被銷毀時會自動刪除它所指之物,所以一定要注意別讓多個auto_ptr指向同一個對象。auto_ptr有個不尋常的性質:若通過copy構造函數或copy assignment操作符復制它們,它們會變成null,而復制所得的指針將取得資源的唯一擁有權。auto_ptr并非管理動態分配資源的神兵利器。
Reference-counting smart pointer(引用計數型智慧指針RCSP)是auto_ptr的一種替代方案。持續追蹤共有多少對象指向某筆資源,并在無人指向它時自動刪除。RCSPs提供的行為類似垃圾回收,不同的是,RCSPs無法打破環狀引用(例如兩個其實已經沒有被使用的對象彼此互指,因而好像還處于“被使用”狀態)。
TR1的tr1::shared_ptr 是一個RCSP。
auto_ptr和tr1::shared_ptr兩者都在其析構函數內做delete而不是delete[]動作。那意味著動態分配而得的array身上使用auto_ptr或tr1::shared_ptr是不明智的。但是這是可以通過編譯的。
//準備知識2 auto_ptr不尋常的性質 std::auto_ptr<Investment> pInv(createInvestment()); //pInv指向createInvestment()返回物 std::auto_ptr<Investment> pInv2(pInv); //現在pInv2指向對象,pInv被置為null pInv = pInv2; //現在pInv指向對象,pInv2被置為null
正文
條款13:以對象管理資源 Use objects to manage resource
獲取資源后立刻放進管理對象內。(資源取得時機便是初始化時機。Resource Acquisition Is Initialization;簡稱RAII)
管理對象運用析構函數確保資源被釋放。
例子如下:
class Investment{...}; Investment* createInvestment(); void f()//auto_ptr版本 { std::auto_ptr<Investment> pInv(createInvestment()); // 調用factory函數,使用pInv經由auto_ptr的析構函數自動刪除pInv ... } void f()//shared_ptr版本 { ... std::tr1::shard_ptr<Investment> pInv(createInvestment()); //pInv指向createInvestment()返回物 std::tr1::shard_ptr<Investment> pInv2(pInv); //現在pInv,pInv2指向同一對象 pInv = pInv2; //無任何改變 ... }
重點:
為防止資源泄漏,請使用RAII對象,它們在構造函數中獲得資源并在析構函數中釋放資源。
兩個常用的RAII classes 分布式tr1::shared_ptr 和auto_ptr。前者通常是較佳選擇,因為其copy行為比較直觀。若選擇auto_ptr,復制動作會使它指向null。
2016-11-03 22:23:43
條款14:在資源管理類中小心copying行為。
當一個RAII對象被復制,有如下幾種可能
禁止復制。
class Lock:private Uncopyable{ };
對底層資源祭出“引用計數法”。有時候我們希望保有資源,直到它的最后一個使用者被銷毀。這種情況下復制RAII對象時,應該將資源的“被引用數”遞增。tr1::shared_ptr便是如此。tr1::shared_ptr允許指定所謂的“刪除器”,那是一個函數或函數對象,當引用次數為0時便被調用。刪除器對tr1::shared_ptr構造函數而言是可有可無的第二參數。
復制底部資源。進行深度拷貝。
轉移底部資源的擁有權。采用auto_ptr。
class Lock{ public: //以某個Mutex初始化shared_ptr,并以unlock函數作為刪除器 explicit Lock(Mutex *pm):mutexPtr(pm,unlock) { lock(mutexPtr.get()); } private: std::tr1::shared_ptr<Mutex> mutexPtr; }
重點:
復制RAII對象必須一并復制它所管理的資源,所以資源的copying行為決定RAII對象的copying行為。
普通而常見的RAII class copying行為是:抑制copying、施行引用計數法。不過其他行為也都可能被實現。
2016-11-03 23:59:30
條款15:在資源管理類中提供對原始資源的訪問。
有時候需要一個函數可將 RAII class 對象轉換為其所內含之原始資源。有兩種做法可以達成目標:
1.顯式轉換
tr1::shared_ptr和auto_ptr都提供一個get成員函數,用來執行顯式轉換,也就是它會返回智能指針內部的原始指針。
例子如下:
std::tr1::shared_ptr<Investment> pInv(createInvestment()); int daysHeld(const Investment* pi); int days = daysHeld(pInv);//錯誤!!! 不允許直接使用智能指針,需要獲取原始資源。 int days = daysHeld(pInv.get());//good!!!將pInv內的原始指針傳給daysHeld
2.隱式轉換
幾乎所有的智能指針都重載了指針取值操作符(operator->和operator*),tr1::shared_ptr和auto_ptr也重載了取值操作符,它們允許隱式轉換至底部原始指針。
例子如下:
class Investment{ public: bool isTaxFree() const; ... }; Investment* createInvestment(); std::tr1::shared_ptr<Investment> pi1(createInvestment()); bool taxable1 = !(pi1->isTaxFree());//經過operator->訪問資源 std::tr1::shared_ptr<Investment> pi2(createInvestment()); bool taxable2 = !((*pi1).isTaxFree());//經過operator*訪問資源
重點:
APIs往往要求訪問原始資源,所以每一個RAII class應該提供一個“取得所管理之資源”的辦法。
對原始資源的訪問可能經由顯式轉換或隱式轉換。一般而言顯式轉換比較安全,但隱式轉換對客戶比較方便。
2016-11-04 17:44:52
條款16:成對使用new和delete時要采取相同形式。
當你使用new,有兩件事發生。第一,通過operator new的函數內存被分配出來。第二,針對此內存會有一個構造函數被調用。
當你使用delete,有兩件事發生。第一,針對此內存會有一個(或多個)析構函數調用。第二,通過operator delete的函數釋放內存。
delete的最大問題在于:即將被刪除的內存之內究竟存有多少個對象?
當你對一個指針使用delete,唯一能夠讓delete知道內存中是否存在一個“數組大小記錄”的辦法就是:由你來告訴它。如果你使用delete時加上中括號[],delete便認定指針指向一個數組,否則它便認定指針指向單一對象。
例子如下:
std::string* stringPtr1 = new std::string; std::string* stringPtr2 = new std::string[100]; ... delete stringPtr1;//刪除一個對象 delete [] stringPtr2;//刪除一個由對象組成的數組
盡量不要對數組形式做typedefs動作。可以使用vector或者string來替代。
重點:
如果你在使用new表達式中使用[],必須在相應的delete表達式中也使用[]。如果你在使用new表達式中不使用[],一定不要在相應的delete表達式中使用[]。
2016-11-04 17:54:51
條款17:以獨立語句將newed對象置于智能指針。
例子:
int priority(); void processWidget(str::tr1::shared_ptr<Widget> pw,int priority);
調用processWidget:
processWidget(new Widget,priority());//不能通過編譯 processWidget(std::tr1::shared_ptr<Widget> (new Widget),priority());//可以通過編譯
調用processWidget之前,編譯器必須創建代碼,做一下3件事:
1.調用priority;
2.執行“new Widget”;
3.調用tr1::shared_ptr構造函數。
由于C++編譯器以什么樣的次序完成這件事,彈性很大。
一種可能的操作順序是2,1,3。但是在執行2后,如果執行1時,發生異常,那么2中返回的指針被遺失。
而且3還沒有來得及執行,所以2返回的指針沒有置入st1::shared_ptr內,所以會發生資源泄漏。
避免這類問題的辦法很簡單:使用分離語句。分別寫出(1)創建Widget;
(2)將它置于一個智能指針內,然后再把那個智能指針傳給processWidget:
std::tr1::shared_ptr<Widget> pw(new Widget); processWidget(pw,priority());
重點:
以獨立語句將newed對象存儲于智能指針內。如果不這樣做,一旦異常被拋出,有可能導致難以察覺的資源泄漏。
2016-11-04 22:56:02
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。