C++異常處理模型除了支援面向過程的C風格程式中的異常處理外(就是沒有物件導向的概念,完全是C程式,整個程式實際就是函數的集合,但卻用C++編譯器來編譯這樣的C程式,所以這樣的程式中是可以a使用C++的異常處理機制的,要不怎麼說C++是相容C語言的呢?但是需要注意的是,單純的C語言程式中是不能使用C++異常處理模型進行編程的。是不是有點說拗口了?有點糊塗了呢?其實很簡單,那就是如果程式中使用了C++異常處理機制,也即代碼中有try、catch和throw關鍵字,那麼就必須使用C++編譯器來編譯這個程式。許多程式員朋友們在這裡有一個理解上的誤區,認為只有程式中使用了物件導向的概念,即使用class關鍵字來定義一個類結構才算得上C++程式,其實這種理解是片面的,如果程式中採用了C++異常處理機制,那麼也有理由認為這是一個C++程式,哪怕程式的代碼完全是C語言風格的,並且這樣的程式用C編譯器來編譯肯定將會報錯,提示未定義的try標示符等等錯誤資訊),還支援物件導向程式中對象拋出的異常處理。
C++異常處理模型的確和物件導向是緊密結合的,除了在相遇篇中介紹到的用對象來描述程式中出現的異常之外,C++異常處理模型也對在物件導向程式中的對象執行個體所拋出的異常作了最完善的支援和處理。也許大家會覺得這很容易,沒什麼了不起的地方。但恰恰相反,實際上這才是C++異常處理模型最成功、最不可思議和最閃光的地方。而且由於C++異常處理模型對物件導向有了很好的支援和相容,才使得C++異常處理模型本身的實現變得特別複雜,因為它需要跟蹤每一個對象的運行情況和狀態(關於C++異常處理模型的實現,會在愛的秘密篇中有詳細剖析)。本文和接下來的幾篇文章將講述當對象執行個體拋出異常時將如何處理。
對象的生命週期一般有三種狀態:構造、運行和析構銷毀。因此對象拋出的異常也有這三種區別。是在物件建構時拋出的呢?還是對象運行時拋出的呢?或是析構對象時拋出的?這三種不同時候拋出的異常會將會產生不同的結果。本文首先討論最常見的一種情況,在對象運行時拋出的異常,也即執行對象的成員函數時出現的異常。
對象的成員函數拋出的異常
1、老方法,看例子先,如下:
class MyTest_Base
{
public:
MyTest_Base (string name = “”) : m_name(name)
{
cout << “構造一個MyTest_Base類型的對象,對象名為:”<
}
virtual ~ MyTest_Base ()
{
cout << “銷毀一個MyTest_Base類型的對象,對象名為:”<
}
void Func() throw()
{
throw std::exception(“故意拋出一個異常,測試!”);
}
void Other() {}
protected:
string m_name;
};
void main()
{
try
{
MyTest_Base obj1(“obj1”);
// 調用這個成員函數將拋出一個異常,注意obj1的解構函式會被執行嗎?如果
// 會,又是在什麼時候被執行呢?
obj1.Func();
obj1.Other();
}
catch(std::exception e)
{
cout << e.what() << endl;
}
catch(...)
{
cout << “unknow exception”<< endl;
}
}
C++程式員不難看出上面的程式的運行結果,如下:
構造一個MyTest_Base類型的對象,對象名為:obj1
銷毀一個MyTest_Base類型的對象,對象名為:obj1
故意拋出一個異常,測試!
從運行結果可以得出如下結論:
(1) 對象的成員函數出現異常時,catch block能捕獲到異常,這一點就像C語言中的普通函數一樣,沒什麼特別的地方;
(2) 對象的成員函數出現異常時,對象的解構函式將會得到執行(這一點很神奇吧!當然在這裡不會做過多研究,在剖析C++異常處理模型的實現時再做詳細的闡述),這裡與C++標準中規定的物件導向的特性是相一致的,構造了的對象就必須保證在適當的地方要析構它,以釋放可能的資源。因此前面說的“C++異常處理模型對物件導向提供了支援和相容”是有根據的。而且注意它的解構函式是在異常處理模組之前執行的,這一點更與C++標準中規定的物件導向的特性是一致的,當對象出了範圍時,它就必須要被析構。
2、把上面的程式小改一下,運行再看結果,如下:
void main()
{
// obj1對象不在trycatch域中,注意它的解構函式在什麼時候被執行?
MyTest_Base obj1(“obj1”);
try
{
// obj2和obj3對象都在trycatch域中,其中obj3.Func()函數被調用,因此
// obj3會拋出異常,特別需要注意的是,obj2的解構函式會被執行嗎?如果
// 會,又是在什麼時候被執行呢?
MyTest_Base obj2(“obj2”), obj3(“obj3”);
obj3.Other();
// 調用這個成員函數將拋出一個異常
obj3.Func();
// 注意:obj4對象在構造之前,函數中就有異常拋出。所以obj4對象將不會
// 被構造,當然也不會被析構
MyTest_Base obj4(“obj4”);
obj3.Other();
}
catch(std::exception e)
{
cout << e.what() << endl;
}
catch(...)
{
cout << “unknow exception”<< endl;
}
}
上面的程式也難看出其運行結果,如下:
構造一個MyTest_Base類型的對象,對象名為:obj1
構造一個MyTest_Base類型的對象,對象名為:obj2
構造一個MyTest_Base類型的對象,對象名為:obj3
銷毀一個MyTest_Base類型的對象,對象名為:obj3
銷毀一個MyTest_Base類型的對象,對象名為:obj2
故意拋出一個異常,測試!
銷毀一個MyTest_Base類型的對象,對象名為:obj1
結合程式中提出的問題和運行結果,可以又可得出如下結論:
(1) 在成員函數出現異常時,同一個範圍中異常出現點後面還未來得及構造的對象將不會被構造,當然也不會被析構;
(2) 在成員函數出現異常時,同一個範圍中異常出現點前面已經構造的對象也同樣會被析構(這是不是更神奇了!)。因此這也顯現出C++異常處理不會破壞C++標準中規定的物件導向的特性,當對象出了範圍時,它就必須要被析構,即便它自己本身沒出現異常,總之不管是正常的執行過程導致對象退出了範圍,還是其它對象運行時發生了異常而導致自己退出了範圍;
(3) 在成員函數出現異常時,未被影響到的其它範圍中的對象將保持自己原來的執行流程。
對象的成員函數拋出的異常時概括性總結
哈哈^-^,其是就只有一句話,那就是“C++的異常處理不會破壞任何一條物件導向的特性!”,因此主人公阿愚建議大家其實無須要記住上面總結的n條結論,記住這一條足矣!