假設(shè)我們有一個(gè)函數(shù)用來展示處理的優(yōu)先級(jí),還有一個(gè)函數(shù),它能夠根據(jù)當(dāng)前優(yōu)先級(jí)的設(shè)置,為一個(gè)動(dòng)態(tài)分配的Widget做一些處理:
int priority();
void processWidget(std::tr1::shared_ptr<Widget> pw, int priority);
一定要時(shí)刻記住“使用對(duì)象管理資源”(參見條目13)。此處,processWidget對(duì)其需要處理的動(dòng)態(tài)分配的Widget使用了一個(gè)智能指針(在這里是一個(gè)tr1::shared_ptr)。
下面是對(duì)progressWidget的一次調(diào)用:
processWidget(new Widget, priority());
請(qǐng)稍等,不要試圖這樣調(diào)用。這將不會(huì)通過編譯。tr1::shared_ptr的構(gòu)造函數(shù)中包含一個(gè)原始指針,這個(gè)構(gòu)造函數(shù)應(yīng)為explicit的,于是便不存在從“new Widget”語(yǔ)句返回的原始指針到processWidget所需的tr1::shared_ptr的隱式轉(zhuǎn)換。然而下邊的代碼將順利通過編譯:
processWidget(std::tr1::shared_ptr<Widget>(new Widget), priority());
看上去有些令人吃驚,盡管我們時(shí)時(shí)處處都使用對(duì)象來管理資源,但是這里還是有可能泄漏資源。了解其中的原由對(duì)深入理解是有一定啟發(fā)性的。
在編譯器能夠生成對(duì)processWidget的調(diào)用之前,它必須對(duì)傳入的參數(shù)進(jìn)行預(yù)先的處理。第二個(gè)參數(shù)僅僅調(diào)用了一個(gè)函數(shù)priority,但是第一個(gè)參數(shù)(“std::tr1::shared_ptr<Widget>(new Widget)”)包含兩部分:
運(yùn)行“new Widget”語(yǔ)句
調(diào)用tr1::shared_ptr的構(gòu)造函數(shù)
因此,我們說在processWidget可以被調(diào)用之前,編譯器必須自動(dòng)生成代碼來解決下面的三件事情:
l 調(diào)用priority。
l 執(zhí)行“new Widget”。
l 調(diào)用tr1::shared_ptr的構(gòu)造函數(shù)。
C++編譯器對(duì)于這三項(xiàng)任務(wù)完成的順序要求得很寬松。(這一點(diǎn)與Java和C#這類語(yǔ)言很不一樣,這類語(yǔ)言中的函數(shù)參數(shù)總是以一個(gè)特定的順序得到預(yù)處理。)由于“new Widget”語(yǔ)句運(yùn)行的結(jié)果是一個(gè)參數(shù)的形式傳遞給tr1::shared_ptr的構(gòu)造函數(shù)的,因此它必須在tr1::shared_ptr的構(gòu)造函數(shù)被調(diào)用之前得到執(zhí)行。但是調(diào)用priority的工作可以放到第一,第二,也可以放在最后。如果編譯器決定第二個(gè)處理它(這樣可以使編譯器生成的代碼更高效),我們就會(huì)得到這樣的執(zhí)行序列:
1. 執(zhí)行“new Widget”。
2. 調(diào)用priority。
3. 調(diào)用tr1::shared_ptr的構(gòu)造函數(shù)。
但是請(qǐng)想象一下:如果調(diào)用priority時(shí)拋出了一個(gè)異常的話,將會(huì)發(fā)生些什么。在這種情況下,由“new Widget”返回的指針將會(huì)丟失。這是因?yàn)檫@一指針并不會(huì)保存在tr1::share_ptr中,然而我們?cè)具€期望利用tr1::shared_ptr來避免資源泄露。這種情況下調(diào)用processWidget可能會(huì)造成資源泄漏。這是因?yàn)椋涸谫Y源被創(chuàng)建(通過 new Widget)以后和將這個(gè)資源轉(zhuǎn)交給一個(gè)資源管理對(duì)象之前的這段時(shí)間內(nèi),有產(chǎn)生異常的可能。
防止這類問題發(fā)生的辦法很簡(jiǎn)單:使用單獨(dú)的語(yǔ)句,創(chuàng)建Widget并將其存入一個(gè)智能指針,然后將這個(gè)智能指針傳遞給processWidget:
std::tr1::shared_ptr<Widget> pw(new Widget);
// 在一個(gè)單獨(dú)的語(yǔ)句中創(chuàng)建Widget
// 將其存入一個(gè)智能指針
processWidget(pw, priority()); // 這樣調(diào)用就不會(huì)泄漏了。
這樣是可行的,因?yàn)榫幾g器為多行語(yǔ)句安排執(zhí)行順序要比單一的語(yǔ)句時(shí)嚴(yán)格得多。由于這段改進(jìn)的代碼中,“new Widget”語(yǔ)句以及tr1::shared_ptr的構(gòu)造函數(shù)將在單獨(dú)的語(yǔ)句中得到調(diào)用,而對(duì)priority的調(diào)用在另一個(gè)單獨(dú)的語(yǔ)句中,所以編譯器就沒有機(jī)會(huì)將對(duì)priority的調(diào)用挪動(dòng)到“new Widget”語(yǔ)句和tr1::shared_ptr的構(gòu)造函數(shù)之間了。
時(shí)刻牢記
l 在智能指針中的由new創(chuàng)建的對(duì)象要在單獨(dú)的語(yǔ)句中保存。如果不這樣做,你的程序會(huì)在拋出異常時(shí)發(fā)生資源泄漏。