异常的概念:
程序遇到了问题非主动退出的情况下我们称之为发生了异常,当一个函数发现自己无法处理的错误时就可以抛出异常,让函数的直接或间接的调用者处理这个错误。
c++中异常的处理方式:
#include<iostream>
using namespace std;
void test1()
{
FILE *fp = fopen("2222.txt", "rb");
if (fp == nullptr){
throw 1;
}
}
int main()
{
try
{
test1();
}
catch (int)
{
cout << "文件打开失败" << endl;
}
system("pause");
return 0;
}
即通过throw抛出一种异常,由catch捕获,try调用函数(可以理解为有可能抛出异常,所以尝试着调用这个函数)
异常的抛出和捕获规则:
1.异常是通过抛出对象所触发的,该对象的类型决定了该由哪个catch模块接受。
2.被选中的处理代码是调用链中与该对象类型匹配且离抛出异常位置最近的那一个。并且接受异常后程序从这个位置继续向下进行(可能导致内存泄露的原因)
3.抛出的异常是对象的一份临时拷贝,会调用拷贝构造函数,会在catch后被销毁。
4.catch(...)可以捕获任意类型的异常,叫做万能捕获,但缺陷是不知道异常错误是什么。
栈展开:
- 首先检查throw本身是否在try块内部,如果是再查找匹配的catch语句。如果有匹配的,则调到catch
的地方进行处理。 - 没有匹配的catch则退出当前函数栈,继续在调用函数的栈中进行查找匹配的catch。
- 如果到达main函数的栈,依旧没有匹配的,则终止程序。上述这个沿着调用链查找匹配的catch子句的
过程称为栈展开。所以实际中我们最后都要加一个catch(...)捕获任意类型的异常,否则当有异常没捕
获,程序就会直接终止。 - 找到匹配的catch子句并处理以后,会继续沿着catch子句后面继续执行。
异常的重新抛出:
有可能单个的catch不能完全处理一个异常,在进行一些校正处理以后,希望再交给更外层的调用链函数来处理,catch则可以通过重新抛出将异常传递给更上层的函数进行处理。
double Division(int a, int b)
{
// 当b == 0时抛出异常
if (b == 0)
{
throw "Division by zero condition!";
}
return (double)a / (double)b;
}
void Func()
{
// 这里可以看到如果发生除0错误抛出异常,另外下面的array没有得到释放。
// 所以这里捕获异常后并不处理异常,异常还是交给外面处理,这里捕获了再
// 重新抛出去。
int* array = new int[10];
try {
int len, time;
cin >> len >> time;
cout << Division(len, time) << endl;
}
catch (...)//异常的重新抛出
{
cout << "delete []" << array << endl;
delete[] array;
throw;
}
// ...
cout << "delete []" << array << endl;
delete[] array;
}
int main()
{
try
{
Func();
}
catch (const char* errmsg)
{
cout << errmsg << endl;
}
return 0;
}
异常安全:最好不要在构造函数和析构函数中使用异常,因为有可能造成类对象未完全构造或析构。