More Effective C++ 條款11 (轉)
條款11:禁止異常資訊(exceptions)傳遞到析構外:namespace prefix = o ns = "urn:schemas--com::office" />
在有兩種情況下會解構函式。第一種是在正常情況下刪除一個,例如物件超出了作用域或被顯式地delete。第二種是異常傳遞的堆疊輾轉開解(stack-unwinding)過程中,由異常處理刪除一個物件。
在上述兩種情況下,呼叫解構函式時異常可能處於啟用狀態也可能沒有處於啟用狀態。遺憾的是沒有辦法在解構函式內部區分出這兩種情況。因此在寫解構函式時你必須保守地假設有異常被啟用,因為如果在一個異常被啟用的同時,解構函式也丟擲異常,並導致控制權轉移到解構函式外,C++將呼叫tenate函式。這個函式的作用正如其名字所表示的:它終止你程式的執行,而且是立即終止,甚至連區域性物件都沒有被釋放。
下面舉一個例子,一個Session類用來跟蹤線上的sessions,session就是執行在從你一登入計算機開始一直到登出出系統為止的這段期間的某種東西。每個Session物件關注的是它建立與釋放的日期與時間:
class Session {
public:
Session();
~Session();
...
private:
static void logCreation(Session *objAddr);
static void logDestruction(Session *objAddr);
};
函式logCreation
和 logDestruction
被分別用於記錄物件的建立與釋放。我們因此可以這樣編寫Session的解構函式:
Session::~Session()
{
logDestruction(this);
}
一切看上去很好,但是如果logDestruction
丟擲一個異常,會發生什麼事呢?異常沒有被Session的解構函式捕獲住,所以它被傳遞到解構函式的呼叫者那裡。但是如果解構函式本身的呼叫就是源自於某些其它異常的丟擲,那麼terminate函式將被自動呼叫,徹底終止你的程式。這不是你所希望發生的事情。程式沒有記錄下釋放物件的資訊,這是不幸的,甚至是一個大麻煩。那麼事態果真嚴重到了必須終止程式執行的地步了麼?如果沒有,你必須防止在logDestruction內丟擲的異常傳遞到Session解構函式的外面。唯一的方法是用try和catch block
s
。一種很自然的做法會這樣編寫函式:
Session::~Session()
{
try {
logDestruction(this);
}
catch (...) {
cerr << "Unable to log destruction of Session "
<< "at address "
<< this
<< ".n";
}
}
但是這樣做並不比你原來的程式碼。如果在catch中呼叫operator<
我們可以在catch中放入try,但是這總得有一個限度,否則會陷入迴圈。因此我們在釋放Session時必須忽略掉所有它丟擲的異常:
Session::~Session()
{
try {
logDestruction(this);
}
catch (...) { }
}
catch表面上好像沒有做任何事情,這是一個假象,實際上它阻止了任何從logDestruction
丟擲的
異常被傳遞到session解構函式的外面。我們現在能高枕無憂了,無論session物件是不是在堆疊輾轉開解(stack unwinding)中被釋放,terminate函式都不會被呼叫。
不允許異常傳遞到解構函式外面還有第二個原因。如果一個異常被解構函式丟擲而沒有在函式內部捕獲住,那麼解構函式就不會完全執行(它會停在丟擲異常的那個地方上)。如果解構函式不完全執行,它就無法完成希望它做的所有事情。例如,我們對session類做一個修改,在建立session時啟動一個事務(
database transaction),終止session時結束這個事務:
Session::Session() // 為了簡單起見,,
{ // 這個建構函式沒有
// 處理異常
logCreation(this);
startTransaction(); // 啟動 database transaction
}
Session::~Session()
{
logDestruction(this);
endTransaction(); // 結束database transaction
}
如果在這裡logDestruction
丟擲一個異常,在session建構函式內啟動的
transaction就沒有被終止。我們也許能夠透過重新調整session解構函式內的函式呼叫順序來消除問題,但是如果endTransaction
也丟擲一個異常,我們除了回到使用try和catch外,別無選擇。
綜上所述,我們知道禁止異常傳遞到解構函式外有兩個原因,第一能夠在異常轉遞的堆疊輾轉開解(stack-unwinding)的過程中,防止terminate被呼叫。第二它能幫助確保解構函式總能完成我們希望它做的所有事情。(如果你仍舊不很信服我所說的理由,可以去看Herb Sutter的文章
Exception-Safe Generic Containers
,特別是“Destructors That Throw and Why They’re Evil”這段)。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-990403/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Effective c++條款11:在operator=中處理“自我賦值”C++賦值
- Effective Modern C++ 系列之 條款2: autoC++
- Effective C++ 條款08_不止於此C++
- [讀書筆記][effective C++]條款30-inline的原理筆記C++inline
- 文獻學習——Making Deduction More Effective in SAT Solvers
- 學懂現代C++——《Effective Modern C++》之轉向現代C++C++
- effective C++ : CHAPTER 8C++APT
- Effective C++筆記C++筆記
- 【Effective Modern C++】索引C++索引
- effective C++筆記1C++筆記
- 《Effective C++》讀書筆記C++筆記
- Effective C++ 4.設計與宣告C++
- Effective C++ 筆記(3)資源管理C++筆記
- 條款01: 視C++為一個語言聯邦C++
- 《Effective C++》閱讀總結(三):資源管理C++
- 《Effective C++》第三版-1. 讓自己習慣C++(Accustoming Yourself to C++)C++
- 學懂現代C++——《Effective Modern C++》之型別推導和autoC++型別
- 條款05: 瞭解c++默默編寫並呼叫哪些函式C++函式
- 《Effective C++》第三版-5. 實現(Implementations)C++
- 讀完Java名著《Effective Java》: 我整理了這50條技巧Java
- 《Effective C++》閱讀總結(四): 設計、宣告與實現C++
- [c++/gcc] Centos 7.9升級 gcc 4.8.5 到 gcc11 [轉]C++GCCentOS
- c++ 11 執行緒池---完全使用c++ 11新特性C++執行緒
- 《Effective C++》第三版-3. 資源管理(Resource Management)C++
- c++切面條題目C++
- 《Effective C++》第三版-4. 設計與宣告(Design and Declarations)C++
- 《Effective Python 第二版》第二條 遵循PEP8風格指南Python
- 《Effective C++》閱讀總結(二):類的構造、析構和賦值C++賦值
- 《看雪服務條款》
- 【C++】C++之型別轉換C++型別
- C++中的條件變數C++變數
- MORE_DETAIL_TECHAI
- TypeScript 之 More on FunctionsTypeScriptFunction
- 11_條件隨機場條件隨機場
- More web function requests go online concurrently, and web service deployment is faster and more economical!WebFunctionGoAST
- C++ 11 新特性 nullptr 學習C++Null
- C++筆記(11) 智慧指標C++筆記指標
- effective java 觀後感Java
- Vegetables need more practice.