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主要用于在将异常传递到堆栈之前记录失败,或者用于更改抛出的异常类型。

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