【C++】异常

C语言处理错误的方式

  • 终止程序,如assert、exit等
  • 返回错误码,这种方式比较常用,但是需要查找对应的错误,不够直观

C++中的异常

异常是一种处理错误的方式,当一个函数发现自己无法处理的错误时就可以抛出异常,让函数的直接或间接的调用者处理这个错误

  • throw: 当问题出现时,程序会抛出一个异常。通过使用 throw 关键字来完成
  • catch: 在想要处理问题的地方,通过异常处理程序捕获异常。catch 关键字用于捕获异常,可以有多个catch进行捕获
  • try: try 块中的代码标识将被激活的特定异常,它后面通常跟着一个或多个 catch 块

异常的抛出和捕获

抛出一个已知类型的异常进行捕获:

double Division(int a, int b)
{
	// 当b == 0时抛出异常;异常抛出后就必须进行捕获
	if (b == 0)
		throw "除数不能为0";	// 抛出的异常可以是任意类型的对象,此处为常量字符串指针
	else
		return (a / b);
}
void Func()
{
	int a, b;
	cin >> a >> b;
	cout << Division(a, b) << endl;
}

int main()
{
	try {
		Func();
	}
	catch (const char* errmsg)		// 捕获抛出的字符串指针
	{
		cout << errmsg << endl;		// 对其及进行输出
	}

	return 0;
}

再抛一个任意类型的异常进行捕获:

double Division(int a, int b)
{
	// 当b == 0时抛出异常;异常抛出后就必须进行捕获
	if (b == 0)
		throw 4;	// 抛出的异常可以是任意类型的对象,此处抛出一个整形,假设此异常类型未知
	else
		return (a / b);
}
void Func()
{
	int a, b;
	cin >> a >> b;
	cout << Division(a, b) << endl;
}

int main()
{
	try {
		Func();
	}
	catch (...)		// 捕获任意类型的异常,防止程序异常终止,但这样捕获不知道具体的错误
	{
		cout << "未知异常" << endl;
	}

	return 0;
}

异常的重新抛出

先来看段代码:

double Division(int a, int b)
{
	// 当b == 0时抛出异常;异常抛出后就必须进行捕获
	if (b == 0)
		throw "除数不能为0";	// 抛出的异常可以是任意类型的对象,此处为常量字符串指针
	else
		return (a / b);
}
void Func()
{
	int* array = new int[10];		// new一个数组

	int a, b;
	cin >> a >> b;
	cout << Division(a, b) << endl;

	delete[] array;					// delete数组
}

int main()
{
	try {
		Func();
	}
	catch (const char* errmsg)		// 捕获抛出的字符串指针
	{
		cout << errmsg << endl;		// 对其及进行输出
	}

	return 0;
}
//在上面这段代码中,运行main函数时先执行Func函数,Func函数先new了一个数组,再调用执行Division函数
//若执行Division函数时发生异常,则会直接跳转到catch处去捕获异常,这时在Func中new的数组并没有delete
//这就造成了内存泄漏问题

此时可以在中间捕获,处理完再抛出:

double Division(int a, int b)
{
	// 当b == 0时抛出异常;异常抛出后就必须进行捕获
	if (b == 0)
		throw "除数不能为0";	// 抛出的异常可以是任意类型的对象,此处为常量字符串指针
	else
		return (a / b);
}
void Func()
{
	int* array = new int[10];		// new一个数组

	try								// 把可能发生异常的代码放到try模块里
	{
		int a, b;
		cin >> a >> b;
		cout << Division(a, b) << endl;
	}
	catch (const char* errmsg)		// 捕获--这里的捕获并不是为了处理异常,而只是为了重新抛出
	{
		delete[] array;				// 处理内存泄漏问题
		throw errmsg;				// 重新抛出异常
	}
	
	delete[] array;					// delete数组
}

int main()
{
	try {
		Func();
	}
	catch (const char* errmsg)		// 捕获抛出的字符串指针
	{
		cout << errmsg << endl;		// 对其及进行输出
	}

	return 0;
}

若上面的try模块可能会抛出多种类型的异常,则需要与之对应的写多种catch模块去捕获不同种类的异常。
因此重新抛出的场景更建议下面这种写法:

double Division(int a, int b)
{
	// 当b == 0时抛出异常;异常抛出后就必须进行捕获
	if (b == 0)
		throw "除数不能为0";	// 抛出的异常可以是任意类型的对象,此处为常量字符串指针
	else
		return (a / b);
}
void Func()
{
	int* array = new int[10];		// new一个数组

	try								// 把可能发生异常的代码放到try模块里
	{
		int a, b;
		cin >> a >> b;
		cout << Division(a, b) << endl;
	}
	
	// 捕获任意类型的异常,在模块内抛出
	catch (...)						// 捕获--这里的捕获并不是为了处理异常,而只是为了重新抛出
	{
		delete[] array;				// 处理内存泄漏问题
		throw ;						// 重新抛出异常
	}
	
	delete[] array;					// delete数组
}

int main()
{
	try {
		Func();
	}
	catch (...)		// 捕获任意类型的异常,防止程序异常终止,但这样捕获不知道具体的错误
	{
		cout << "未知异常" << endl;
	}

	return 0;
}

异常安全

有些地方抛异常可以解决问题,而有些地方抛异常可能会带来问题。

  • 在构造函数中抛异常,可能会导致初始化不完整
  • 在析构函数中抛异常可能会导致资源清理不完全,从而引发内存泄漏
  • 在new和delete中间抛异常,可能会导致内存泄漏
  • 在lock和unlock之间抛异常,可能会导致死锁问题

异常的优缺点

优点:

  • 相比错误码的方式可以清晰准确的展示出错误的各种信息,这样可以帮助更好的定位程序的bug
  • 部分函数使用异常更好处理,比如构造函数没有返回值,不方便使用错误码方式处理

缺点:

  • 异常会导致程序的执行流乱跳,非常的混乱;运行时出错抛异常会乱跳,导致跟踪调试以及分析程序时比较困难
  • 有了异常非常容易导致内存泄漏、死锁等异常安全问题
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值