[C++系列]C++是如何进行异常处理的?终于能够看懂代码错误原因了

1. C++异常概念

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

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

2. 异常的使用

  1. 宜昌市通过抛出对象而引发的,该对象的类型决定了应该激活那个catch的处理代码。
  2. 被选中的处理代码是调用链中与该对象类型匹配且离抛出异常位置最近的那一个(就近原则)。
  3. catch(…) 可以捕获任意类型的异常,问题是不知道异常错误是什么。
  4. 实际中抛出和捕获的匹配原则有个例外,并不都是类型完全匹配,可以抛出的派生类对象,使用基类捕获,这个在实际中非常实用。

在函数调用链中异常栈展开匹配原则:

  • 首先检查throw本身是否在try块内部,如果是在查找匹配的catch语句,如果有匹配的,则调到catch的地方进行处理
  • 没有匹配的catch则退出当前函数栈,继续在调用函数的栈中进行查找匹配的catch
  • 如果达到main函数的栈,依旧没有匹配的,则终止程序。
  • 找到匹配的catch子句并处理以后,会继续沿着catch子句后面继续执行。
  • catch也可以重新抛出讲异常传递给更上层的函数进行处理。

3. 异常安全

  • 构造函数完成对象的构造和初始化,最好不要在构造函数中抛出异常,否则可能导致对象不完整或没有完全初始化
  • 析构函数主要完成资源的清理,最好不要在析构函数内抛出异常,否则可能导致资源泄漏
  • C++经常会实用RAII来解决以上的问题,或者说是在重新抛出异常之前,执行必须执行的必要的操作。
  • 异常规范:
// 这里表示这个函数会抛出A/B/C/D中的某种类型的异常
void fun() throw(A,B,C,D);
// 这里表示这个函数只会抛出bad_alloc的异常
void* operator new (std::size_t size) throw (std::bad_alloc);
// 这里表示这个函数不会抛出异常
void* operator new (std::size_t size, void* ptr) throw();

4. 自定义异常体系

实际使用中会自定义自己的异常体系进行规范的异常管理,因为一个项目中如果大家随意抛异常,那么外层的调用者将无法进行,因此会定义一套继承的规范体系,这样大家抛出的都是继承的派生类对象,我们只需要捕获一个基类就可以了。(也就是如果我们捕获基类,它会自动向下寻找,直到找到我们锁需要的异常接收类型)
在这里插入图片描述

	try
	{
		vector<double> vec;
		//vec.at(10) = 2.0;
		//vec.resize(0x3fffffff);
		char* ptr = new char[0x7fffffff];
	}
	//用所有异常的根基类的引用或者指针进行捕获,可以匹配所有继承体系的所有类型
	//通过根基类虚函数重写,完成多态的逻辑,最终产生多态的行为,完成异常的精确处理
	catch (exception& e)
	{
		cout << e.what() << endl;
	}
	catch (...)
	{
		cout << "未知异常" << endl;
	}

猜你喜欢

转载自blog.csdn.net/Luckily0818/article/details/107769952