溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

《Effective C++》 讀書筆記之三 資源管理

發(fā)布時(shí)間:2020-08-02 10:51:18 來源:網(wǎng)絡(luò) 閱讀:304 作者:313119992 欄目:編程語言

《Effective C++》 讀書筆記之三 資源管理

準(zhǔn)備知識(shí):

  1. 所謂資源就是,一旦用了它,將來必須還給系統(tǒng)。最常用的資源是動(dòng)態(tài)分配內(nèi)存,其他常見的資源有文件描述器、互斥鎖、圖形界面的字形和筆刷、數(shù)據(jù)庫(kù)連接以及網(wǎng)絡(luò)sockets。

  2. auto_ptr 是個(gè)“類指針對(duì)象”,就是所謂的智能指針,其析構(gòu)函數(shù)自動(dòng)對(duì)其所指對(duì)象調(diào)用delete。auto_ptr位于 #include <memory> 頭文件。由于auto_ptr被銷毀時(shí)會(huì)自動(dòng)刪除它所指之物,所以一定要注意別讓多個(gè)auto_ptr指向同一個(gè)對(duì)象。auto_ptr有個(gè)不尋常的性質(zhì):若通過copy構(gòu)造函數(shù)或copy assignment操作符復(fù)制它們,它們會(huì)變成null,而復(fù)制所得的指針將取得資源的唯一擁有權(quán)。auto_ptr并非管理動(dòng)態(tài)分配資源的神兵利器。

  3. Reference-counting smart pointer(引用計(jì)數(shù)型智慧指針RCSP)是auto_ptr的一種替代方案。持續(xù)追蹤共有多少對(duì)象指向某筆資源,并在無人指向它時(shí)自動(dòng)刪除。RCSPs提供的行為類似垃圾回收,不同的是,RCSPs無法打破環(huán)狀引用(例如兩個(gè)其實(shí)已經(jīng)沒有被使用的對(duì)象彼此互指,因而好像還處于“被使用”狀態(tài))。

  4. TR1的tr1::shared_ptr 是一個(gè)RCSP。

  5. auto_ptr和tr1::shared_ptr兩者都在其析構(gòu)函數(shù)內(nèi)做delete而不是delete[]動(dòng)作。那意味著動(dòng)態(tài)分配而得的array身上使用auto_ptr或tr1::shared_ptr是不明智的。但是這是可以通過編譯的。

//準(zhǔn)備知識(shí)2 auto_ptr不尋常的性質(zhì)

std::auto_ptr<Investment> pInv(createInvestment());
//pInv指向createInvestment()返回物
std::auto_ptr<Investment> pInv2(pInv);
//現(xiàn)在pInv2指向?qū)ο?,pInv被置為null
pInv = pInv2;
//現(xiàn)在pInv指向?qū)ο?,pInv2被置為null



正文


條款13:以對(duì)象管理資源 Use objects to manage resource

獲取資源后立刻放進(jìn)管理對(duì)象內(nèi)。(資源取得時(shí)機(jī)便是初始化時(shí)機(jī)。Resource Acquisition Is Initialization;簡(jiǎn)稱RAII)

管理對(duì)象運(yùn)用析構(gòu)函數(shù)確保資源被釋放。

例子如下:

class Investment{...};
Investment* createInvestment();

void f()//auto_ptr版本
{
	std::auto_ptr<Investment> pInv(createInvestment());
	// 調(diào)用factory函數(shù),使用pInv經(jīng)由auto_ptr的析構(gòu)函數(shù)自動(dòng)刪除pInv
	...
}


void f()//shared_ptr版本
{
	...
	std::tr1::shard_ptr<Investment> pInv(createInvestment());
	//pInv指向createInvestment()返回物
	std::tr1::shard_ptr<Investment> pInv2(pInv);
	//現(xiàn)在pInv,pInv2指向同一對(duì)象
	pInv = pInv2;
	//無任何改變
	...
}


重點(diǎn):

  1. 為防止資源泄漏,請(qǐng)使用RAII對(duì)象,它們?cè)跇?gòu)造函數(shù)中獲得資源并在析構(gòu)函數(shù)中釋放資源。

  2. 兩個(gè)常用的RAII classes 分布式tr1::shared_ptr 和auto_ptr。前者通常是較佳選擇,因?yàn)槠鋍opy行為比較直觀。若選擇auto_ptr,復(fù)制動(dòng)作會(huì)使它指向null。

2016-11-03 22:23:43


條款14:在資源管理類中小心copying行為。

當(dāng)一個(gè)RAII對(duì)象被復(fù)制,有如下幾種可能

  1. 禁止復(fù)制。

    class Lock:private Uncopyable{ };

  2. 對(duì)底層資源祭出“引用計(jì)數(shù)法”。有時(shí)候我們希望保有資源,直到它的最后一個(gè)使用者被銷毀。這種情況下復(fù)制RAII對(duì)象時(shí),應(yīng)該將資源的“被引用數(shù)”遞增。tr1::shared_ptr便是如此。tr1::shared_ptr允許指定所謂的“刪除器”,那是一個(gè)函數(shù)或函數(shù)對(duì)象,當(dāng)引用次數(shù)為0時(shí)便被調(diào)用。刪除器對(duì)tr1::shared_ptr構(gòu)造函數(shù)而言是可有可無的第二參數(shù)。

  3. 復(fù)制底部資源。進(jìn)行深度拷貝。

  4. 轉(zhuǎn)移底部資源的擁有權(quán)。采用auto_ptr。

class Lock{
public:
    //以某個(gè)Mutex初始化shared_ptr,并以u(píng)nlock函數(shù)作為刪除器
    explicit Lock(Mutex *pm):mutexPtr(pm,unlock)
    {
        lock(mutexPtr.get());
    }
private:
    std::tr1::shared_ptr<Mutex> mutexPtr;
}


重點(diǎn):

  1. 復(fù)制RAII對(duì)象必須一并復(fù)制它所管理的資源,所以資源的copying行為決定RAII對(duì)象的copying行為。

  2. 普通而常見的RAII class copying行為是:抑制copying、施行引用計(jì)數(shù)法。不過其他行為也都可能被實(shí)現(xiàn)。

2016-11-03 23:59:30

條款15:在資源管理類中提供對(duì)原始資源的訪問。

有時(shí)候需要一個(gè)函數(shù)可將 RAII class 對(duì)象轉(zhuǎn)換為其所內(nèi)含之原始資源。有兩種做法可以達(dá)成目標(biāo):

1.顯式轉(zhuǎn)換

tr1::shared_ptr和auto_ptr都提供一個(gè)get成員函數(shù),用來執(zhí)行顯式轉(zhuǎn)換,也就是它會(huì)返回智能指針內(nèi)部的原始指針。

例子如下:

std::tr1::shared_ptr<Investment> pInv(createInvestment());

int daysHeld(const Investment* pi);

int days = daysHeld(pInv);//錯(cuò)誤!??! 不允許直接使用智能指針,需要獲取原始資源。

int days = daysHeld(pInv.get());//good!!!將pInv內(nèi)的原始指針傳給daysHeld


2.隱式轉(zhuǎn)換

幾乎所有的智能指針都重載了指針取值操作符(operator->和operator*),tr1::shared_ptr和auto_ptr也重載了取值操作符,它們?cè)试S隱式轉(zhuǎn)換至底部原始指針。

例子如下:

class Investment{
public:
	bool isTaxFree() const;
	...
};

Investment* createInvestment();
std::tr1::shared_ptr<Investment> pi1(createInvestment());
bool taxable1 = !(pi1->isTaxFree());//經(jīng)過operator->訪問資源

std::tr1::shared_ptr<Investment> pi2(createInvestment());

bool taxable2 = !((*pi1).isTaxFree());//經(jīng)過operator*訪問資源


重點(diǎn):

  1. APIs往往要求訪問原始資源,所以每一個(gè)RAII class應(yīng)該提供一個(gè)“取得所管理之資源”的辦法。

  2. 對(duì)原始資源的訪問可能經(jīng)由顯式轉(zhuǎn)換或隱式轉(zhuǎn)換。一般而言顯式轉(zhuǎn)換比較安全,但隱式轉(zhuǎn)換對(duì)客戶比較方便。

2016-11-04 17:44:52

條款16:成對(duì)使用new和delete時(shí)要采取相同形式。

當(dāng)你使用new,有兩件事發(fā)生。第一,通過operator new的函數(shù)內(nèi)存被分配出來。第二,針對(duì)此內(nèi)存會(huì)有一個(gè)構(gòu)造函數(shù)被調(diào)用。

當(dāng)你使用delete,有兩件事發(fā)生。第一,針對(duì)此內(nèi)存會(huì)有一個(gè)(或多個(gè))析構(gòu)函數(shù)調(diào)用。第二,通過operator delete的函數(shù)釋放內(nèi)存。

delete的最大問題在于:即將被刪除的內(nèi)存之內(nèi)究竟存有多少個(gè)對(duì)象?

當(dāng)你對(duì)一個(gè)指針使用delete,唯一能夠讓delete知道內(nèi)存中是否存在一個(gè)“數(shù)組大小記錄”的辦法就是:由你來告訴它。如果你使用delete時(shí)加上中括號(hào)[],delete便認(rèn)定指針指向一個(gè)數(shù)組,否則它便認(rèn)定指針指向單一對(duì)象。

例子如下:

std::string* stringPtr1 = new std::string;
std::string* stringPtr2 = new std::string[100];
...
delete stringPtr1;//刪除一個(gè)對(duì)象
delete [] stringPtr2;//刪除一個(gè)由對(duì)象組成的數(shù)組


盡量不要對(duì)數(shù)組形式做typedefs動(dòng)作??梢允褂胿ector或者string來替代。

重點(diǎn):

如果你在使用new表達(dá)式中使用[],必須在相應(yīng)的delete表達(dá)式中也使用[]。如果你在使用new表達(dá)式中不使用[],一定不要在相應(yīng)的delete表達(dá)式中使用[]。

2016-11-04 17:54:51


條款17:以獨(dú)立語句將newed對(duì)象置于智能指針。

例子:

int priority();
void processWidget(str::tr1::shared_ptr<Widget> pw,int priority);

調(diào)用processWidget:

processWidget(new Widget,priority());//不能通過編譯
processWidget(std::tr1::shared_ptr<Widget> (new Widget),priority());//可以通過編譯

調(diào)用processWidget之前,編譯器必須創(chuàng)建代碼,做一下3件事:

1.調(diào)用priority;

2.執(zhí)行“new Widget”;

3.調(diào)用tr1::shared_ptr構(gòu)造函數(shù)。


由于C++編譯器以什么樣的次序完成這件事,彈性很大。

一種可能的操作順序是2,1,3。但是在執(zhí)行2后,如果執(zhí)行1時(shí),發(fā)生異常,那么2中返回的指針被遺失。

而且3還沒有來得及執(zhí)行,所以2返回的指針沒有置入st1::shared_ptr內(nèi),所以會(huì)發(fā)生資源泄漏。


避免這類問題的辦法很簡(jiǎn)單:使用分離語句。分別寫出(1)創(chuàng)建Widget;

(2)將它置于一個(gè)智能指針內(nèi),然后再把那個(gè)智能指針傳給processWidget:

std::tr1::shared_ptr<Widget> pw(new Widget);
processWidget(pw,priority());


重點(diǎn):

以獨(dú)立語句將newed對(duì)象存儲(chǔ)于智能指針內(nèi)。如果不這樣做,一旦異常被拋出,有可能導(dǎo)致難以察覺的資源泄漏。

2016-11-04 22:56:02

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI