做網(wǎng)站的價位附近廣告公司聯(lián)系電話
3.資源管理
條款13:以對象管理資源
以對象管理資源對于傳統(tǒng)的堆資源管理,我們需要使用成對的new和delete,這樣若忘記delete就會造成內(nèi)存泄露。因此,我們應(yīng)盡可能以對象管理資源,并采用RAII(Resource Acquisition Is Initialize,資源取得時機便是初始化時機),讓析構(gòu)函數(shù)負(fù)責(zé)資源的釋放。
原書此處提到的auto_ptr的內(nèi)容已經(jīng)過時,在 C++11 中,通過專一所有權(quán)來管理RAII對象可以使用std::unique_ptr,通過引用計數(shù)來管理RAII對象可以使用std::shared_ptr。
std::unique_ptr<Investment> pUniqueInv1(CreateInvestment());
std::unique_ptr<Investment> pUniqueInv2(std::move(pUniqueInv1)); // 轉(zhuǎn)移資源所有權(quán)std::shared_ptr<Investment> pSharedInv1(CreateInvestment());
std::shared_ptr<Investment> pSharedInv2(pSharedInv1); // 引用計數(shù)+1
智能指針默認(rèn)會自動delete所持有的對象,我們也可以為智能指針指定所管理對象的釋放方式(刪除器deleter):
std::unique_ptr<Investment, decltype(GetRidOfInvestment)*> pUniqueInv(CreateInvestment(), GetRidOfInvestment);
std::shared_ptr<Investment> pSharedInv(CreateInvestment(), GetRidOfInvestment);
- 為防止資源泄露,請使用RAII對象,它們在構(gòu)造函數(shù)中獲得資源并在析構(gòu)函數(shù)中析構(gòu)函數(shù)中釋放資源
- 兩個常被使用的RAII classes分別是shared_ptr和unique_ptr。前者通常是較佳的選擇,因為其copy行為比較直觀。
條款14:在資源管理類中小心拷貝行為
我們應(yīng)該永遠(yuǎn)保持這樣的思考:當(dāng)一個RAII對象被復(fù)制,會發(fā)生什么事?
選擇一:禁止復(fù)制
許多時候允許RAII對象被復(fù)制并不合理,如果確是如此,那么就該明確禁止復(fù)制行為,條款 6 已經(jīng)闡述了怎么做這件事。
選擇二:對底層資源祭出“引用計數(shù)法”
正如std::shared_ptr所做的那樣,每一次復(fù)制對象就使引用計數(shù)+1,每一個對象離開定義域就調(diào)用析構(gòu)函數(shù)使引用計數(shù)-1,直到引用計數(shù)為0就徹底銷毀資源。
選擇三:復(fù)制底層資源
在復(fù)制對象的同時復(fù)制底層資源的行為又被稱作深拷貝(Deep copying),例如在一個對象中有一個指針,那么在復(fù)制這個對象時就不能只復(fù)制指針,也要復(fù)制指針?biāo)赶虻臄?shù)據(jù)。
選擇四:轉(zhuǎn)移底層資源的所有權(quán)
和std::unique_ptr的行為類似,永遠(yuǎn)保持只有一個對象擁有對資源的管理權(quán),當(dāng)需要復(fù)制對象時轉(zhuǎn)移資源的管理權(quán)。
條款15:在資源管理類中提供對原始資源的訪問
Investment* pRaw = pSharedInv.get(); // 顯式訪問原始資源
Investment raw = *pSharedInv; // 隱式訪問原始資源
當(dāng)我們在設(shè)計自己的資源管理類時,也要考慮在提供對原始資源的訪問時,是使用顯式訪問還是隱式訪問的方法,還是兩者皆可。
class Font {
public:FontHandle Get() const { return handle; } // 顯式轉(zhuǎn)換函數(shù)operator FontHandle() const { return handle; } // 隱式轉(zhuǎn)換函數(shù)private:FontHandle handle;
};
- APIs往往要求訪問原始資源,所以每一個RAII class應(yīng)該提供一個“取得其所管理之資源”的方法
- 對原始資源的訪問可能經(jīng)由顯示轉(zhuǎn)換或隱式轉(zhuǎn)換。一般而言顯示轉(zhuǎn)換比較安全,但隱式轉(zhuǎn)換對客戶比較方便。
條款16:成對使用new和delete要采取相同形式
使用new來分配單一對象,使用new[]來分配對象數(shù)組,必須明確它們的行為并不一致,分配對象數(shù)組時會額外在內(nèi)存中記錄“數(shù)組大小”,而使用delete[]會根據(jù)記錄的數(shù)組大小多次調(diào)用析構(gòu)函數(shù),使用delete則僅僅只會調(diào)用一次析構(gòu)函數(shù)。對于單一對象使用delete[]其結(jié)果也是未定義的,程序可能會讀取若干內(nèi)存并將其錯誤地解釋為數(shù)組大小。
int* array = new int[10];
int* object = new int;delete[] array;
delete object;
需要注意的是,使用typedef定義數(shù)組類型會帶來額外的風(fēng)險:
最好不要對數(shù)組形式做typedef動作
typedef std::string AddressLines[4];std::string* pal = new AddressLines; // pal 是一個對象數(shù)組,而非單一對象delete pal; // 行為未定義
delete[] pal; // 正確
- 如果你在new表達(dá)式中使用[ ],必須在相應(yīng)的delete表達(dá)式中也使用[ ]。如果你在new表達(dá)式中不適用[], 一定不要在相應(yīng)的delete表達(dá)式中使用[ ]
條款17:以獨立語句將 new出的對象置入智能指針
原書此處所講已過時,現(xiàn)在更好的做法是使用std::make_unique和std::make_shared:
auto pUniqueInv = std::make_unique<Investment>(); // since C++14
auto pSharedInv = std::make_shared<Investment>(); // since C++11
參考:知乎