C++基礎教程面向對象(學習筆記(79))

函數try塊

在大多數情況下,try和catch塊工作得很好,但有一個特殊情況,它們是不夠的。請考慮以下示例:

class A
{
private:
	int m_x;
public:
	A(int x) : m_x(x)
	{
		if (x <= 0)
			throw 1;
	}
};
 
class B : public A
{
public:
	B(int x) : A(x)
	{
		// 如果A的創建失敗並且我們想在這裏處理它會發生什麼?
	}
};
 
int main()
{
	try
	{
		B b(0);
	}
	catch (int)
	{
		std::cout << "Oops\n";
	}
}

在上面的例子中,派生類B調用基類構造函數A,它可以引發異常。因爲對象b的創建已經放在try塊中(在函數main()中),如果A拋出異常,main的try塊將捕獲它。因此,該程序打印:

Oops
但是如果我們想要捕獲B內部的異常怎麼辦?在調用B構造函數的主體之前,對構造函數A的調用通過成員初始化列表進行。沒有辦法圍繞它包裝一個標準的try塊。

在這種情況下,我們必須使用一個稍微修改過的try塊,稱爲函數try塊。

函數try塊

函數try塊旨在允許您在整個函數的主體周圍建立異常處理程序,而不是圍繞代碼塊。

函數try塊的語法有點難以描述,因此我們將通過示例顯示:

#include <iostream>
 
class A
{
private:
	int m_x;
public:
	A(int x) : m_x(x)
	{
		if (x <= 0)
			throw 1;
	}
};
 
class B : public A
{
public:
	B(int x) try : A(x) // 注意在這裏添加try關鍵字
	{
	}
	catch (...) //請注意,這與函數本身處於相同的縮進級別
	{
                // 此處捕獲成員初始化程序列表或構造函數體的異常
 
                std::cerr << "Construction of A failed\n";
 
                // 如果未在此處顯式拋出異常,則將隱式重新拋出當前異常
	}
};
 
int main()
{
	try
	{
		B b(0);
	}
	catch (int)
	{
		std::cout << "Oops\n";
	}
}

運行此程序時,它會生成輸出:

Construction of A failed
Oops
讓我們更詳細地研究一下這個程序。

首先,請注意在成員初始化列表之前添加“try”關鍵字。這表明應該在try塊內部考慮該點之後的所有內容(直到函數結束)。

其次,請注意關聯的catch塊與整個函數處於相同的縮進級別。try關鍵字和函數體末尾之間拋出的任何異常都有資格在此處捕獲。

最後,與普通的catch塊不同,它允許您使用函數級try塊解析異常,拋出新異常或重新拋出現有異常,您必須拋出或重新拋出異常。如果您沒有顯式拋出新異常,或者重新拋出當前異常(使用throw關鍵字本身),異常將在堆棧中隱式重新拋出。

在上面的程序中,因爲我們沒有從函數級catch塊中顯式拋出異常,所以異常被隱式重新拋出,並被main()中的catch塊捕獲。這就是上述程序打印“糟糕”的原因!

雖然函數級別try塊也可以與非成員函數一起使用,但它們通常不是因爲很少需要這樣的情況。它們幾乎只與構造函數一起使用!

不要使用函數嘗試清理資源

當構造對象失敗時,不會調用類的析構函數。因此,您可能會嘗試使用函數try塊作爲清除在失敗之前部分分配資源的類的方法。但是,引用失敗對象的成員被認爲是未定義的行爲,因爲該對象在執行catch塊之前是“死”。這意味着您無法使用函數嘗試在類之後進行清理。

函數try主要用於在將異常傳遞到堆棧之前記錄失敗,或者用於更改拋出的異常類型。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章