【C++】异常处理 ⑦ ( 异常类的继承层次结构 | 抛出 / 捕获 多个类型异常对象 | 抛出子类异常对象 / 捕获并处理 父类异常对象 )

2023-12-04 08:47:38 浏览数 (2)

自定义的 异常类 , 可能存在 继承结构 ,

也就是说 在 同一个 try-catch 代码块中 , 如果需要 拦截 和 处理多个 异常时 ,

如果 这些异常都继承相同的父类 , 只需要拦截一个 父类异常即可 ,

本篇博客中 , 讨论 抛出 / 捕获 的 异常类 存在 继承结构 的情况 ;

一、抛出 / 捕获 多个类型异常对象


1、抛出 / 捕获 多个类型异常对象

定义一个函数 , 传入一个 int 类型的参数 , void fun(int a) , 判定传入的参数值大小 ;

只有参数为 60 的时候 , 符合要求 , 其它情况下 , 一律抛出异常 ,

  • 如果参数为负数 , 抛出 eNegative 异常 ;
  • 如果参数为 0 , 抛出 eZero 异常 ;
  • 如果参数 小于 60 , 抛出 eTooSmall 异常 ;
  • 如果参数 大于 60 , 抛出 eTooBig 异常 ;

首先 , 定义上述异常类 , 定义 4 个 自定义异常类 ;

代码语言:javascript复制
class eNegative {};
class eZero {};
class eTooBig {};
class eTooSmall {};

然后 , 在函数中 , 根据不同的判断 , 抛出不同的异常 ,

代码语言:javascript复制
// 1. 在 函数 中 抛出异常
void fun(int a) {
	// 判定数字大小, 只有 60 时是合法的
	// 只要传入的参数不是 60 就需要抛出不同的异常
	if (a == 60) {
		// 合法
	}
	else if (a < 0) {
		throw eNegative();
	}
	else if (a == 0) {
		throw eZero();
	}
	else if (a < 60) {
		throw eTooSmall();
	}
	else if (a > 60) {
		throw eTooBig();
	}
}

最后 , 在 try-catch 代码块中 , 捕获异常 , 需要将 4 个异常 , 各自捕获一次 ;

代码语言:javascript复制
	// 2. 捕获并处理异常
	try
	{
		// 调用可能产生异常的函数
		fun(0);
	}
	catch (eNegative& e) {
		cout << "参数是负数" << endl;
	}
	catch (eZero & e) {
		cout << "参数是 0" << endl;
	}
	catch (eTooSmall & e) {
		cout << "参数太小" << endl;
	}
	catch (eTooBig & e) {
		cout << "参数太大" << endl;
	}
	catch (...) {
		cout << "未知异常" << endl;
	}

2、操作弊端

上述操作 , 有一个弊端 , 那就是每次拦截处理异常时 , 都要手动编写多个 catch 分支 , 每个 catch 分支都要进行各自的操作 ;

如果要在多个位置 , 拦截处理异常 , 则需要编写的代码就太多了 ; 后期维护起来很复杂 ;

3、完整代码示例

代码示例 :

代码语言:javascript复制
#include "iostream"
using namespace std;

class eSize {};
class eNegative {};
class eZero {};
class eTooBig {};
class eTooSmall {};

// 1. 在 函数 中 抛出异常
void fun(int a) {
	// 判定数字大小, 只有 60 时是合法的
	// 只要传入的参数不是 60 就需要抛出不同的异常
	if (a == 60) {
		// 合法
	}
	else if (a < 0) {
		throw eNegative();
	}
	else if (a == 0) {
		throw eZero();
	}
	else if (a < 60) {
		throw eTooSmall();
	}
	else if (a > 60) {
		throw eTooBig();
	}
}

int main() {

	// 2. 捕获并处理异常
	try
	{
		// 调用可能产生异常的函数
		fun(0);
	}
	catch (eNegative& e) {
		cout << "参数是负数" << endl;
	}
	catch (eZero & e) {
		cout << "参数是 0" << endl;
	}
	catch (eTooSmall & e) {
		cout << "参数太小" << endl;
	}
	catch (eTooBig & e) {
		cout << "参数太大" << endl;
	}
	catch (...) {
		cout << "未知异常" << endl;
	}

	cout << "try-catch 代码块执行完毕" << endl;

	// 控制台暂停 , 按任意键继续向后执行
	system("pause");

	return 0;
};

执行结果 :

参数是 0 try-catch 代码块执行完毕 请按任意键继续. . .

二、异常类的继承层次结构


1、抛出子类异常对象 / 捕获并处理 父类异常对象

如果 抛出 / 捕获 多个类型的异常对象 , 每次拦截处理异常时 , 都要手动编写多个 catch 分支 , 不利于代码维护 ;

如果将 相似类型的异常 都继承自 一个父类 , 那么每次拦截时 , 只需要拦截一个父类异常即可 ;

定义父类异常 , 其中定义一个纯虚函数 , 该纯虚函数是异常打印 , 或者异常处理的通用操作 ;

代码语言:javascript复制
class eSize {
public:
	virtual void printError() = 0;
};

剩余的 4 个异常类 , 都继承 异常类父类 , 并实现纯虚函数 ;

代码语言:javascript复制
class eNegative : public eSize {
public:
	virtual void printError() {
		cout << "参数是负数" << endl;
	}
};
class eZero : public eSize {
public:
	virtual void printError() {
		cout << "参数是 0" << endl;
	}
};
class eTooBig : public eSize {
public:
	virtual void printError() {
		cout << "参数太大" << endl;
	}
};
class eTooSmall : public eSize {
public:
	virtual void printError() {
		cout << "参数太小" << endl;
	}
};

上述定义的纯虚函数 , 会发生多态 ;

在拦截父类对象时 , 调用不同的 异常对象 , 会分别调用不同子类的 虚函数方法 ;

抛出异常的函数如下 , 抛出异常时 , 需要抛出子类异常对象 ;

代码语言:javascript复制
// 1. 在 函数 中 抛出异常
void fun(int a) {
	// 判定数字大小, 只有 60 时是合法的
	// 只要传入的参数不是 60 就需要抛出不同的异常
	if (a == 60) {
		// 合法
	}
	else if (a < 0) {
		throw eNegative();
	}
	else if (a == 0) {
		throw eZero();
	}
	else if (a < 60) {
		throw eTooSmall();
	}
	else if (a > 60) {
		throw eTooBig();
	}
}

捕获并处理异常时 , 只需要拦截 父类异常对象即可 ;

代码语言:javascript复制
	// 2. 捕获并处理异常
	try
	{
		// 调用可能产生异常的函数
		fun(0);
	}
	catch (eSize& e) {
		e.printError();
	}
	catch (...) {
		cout << "未知异常" << endl;
	}

2、完整代码示例 - 抛出子类异常对象 / 捕获并处理 父类异常对象

代码示例 :

代码语言:javascript复制
#include "iostream"
using namespace std;

class eSize {
public:
	virtual void printError() = 0;
};
class eNegative : public eSize {
public:
	virtual void printError() {
		cout << "参数是负数" << endl;
	}
};
class eZero : public eSize {
public:
	virtual void printError() {
		cout << "参数是 0" << endl;
	}
};
class eTooBig : public eSize {
public:
	virtual void printError() {
		cout << "参数太大" << endl;
	}
};
class eTooSmall : public eSize {
public:
	virtual void printError() {
		cout << "参数太小" << endl;
	}
};

// 1. 在 函数 中 抛出异常
void fun(int a) {
	// 判定数字大小, 只有 60 时是合法的
	// 只要传入的参数不是 60 就需要抛出不同的异常
	if (a == 60) {
		// 合法
	}
	else if (a < 0) {
		throw eNegative();
	}
	else if (a == 0) {
		throw eZero();
	}
	else if (a < 60) {
		throw eTooSmall();
	}
	else if (a > 60) {
		throw eTooBig();
	}
}

int main() {

	// 2. 捕获并处理异常
	try
	{
		// 调用可能产生异常的函数
		fun(0);
	}
	catch (eSize& e) {
		e.printError();
	}
	catch (...) {
		cout << "未知异常" << endl;
	}

	cout << "try-catch 代码块执行完毕" << endl;

	// 控制台暂停 , 按任意键继续向后执行
	system("pause");

	return 0;
};

执行结果 :

参数是 0 try-catch 代码块执行完毕 请按任意键继续. . .

0 人点赞