异常的介绍
异常是C++1中的一种处理错误的方式,当某一函数发现自己无法处理的错误时就可以抛出异常,让函数的直接或间接的调用者处理这个错误,异常的使用可以避免一些难以发现的bug被更好的发现并被处理。
异常的使用
异常的使用包含是三个部分,分别是:throw、try{}、catch(){}
- throw:当程序问题出现时,通过使用 throw 关键字来抛出一个异常。
- try{}:try {}中放置可能抛出异常的代码,try 块中的代码被称为保护代码。某一块抛出一个异常,捕获异常的方法会使用 try 和 catch 关键字。
- catch(){}:catch在try之后使用,()中放的是throw抛出的异常类型,catch 关键字用于捕获throw关键字抛出的异常,可以有多个catch进行捕获,应对不同的情况。
int Div(int m, int n)
{
if (n == 0)
{
throw string("除数不能为0");
}
return m / n;
}
void test()
{
try
{
int m, n;
cin >> m >> n;
cout << Div(m, n) << endl;
}
catch (string& err)//捕获抛出异常类型为string
{
cout << err << endl;
}
catch (int err)//捕获抛出异常类型为int
{
{
cout << err << endl;
}
catch (...)//捕获没有匹配的任意类型的异常,防止没有捕获异常程序终止
{
cout << "未知异常" << endl;
}
}
异常的注意事项
- 异常是通过throw抛出对象而引发的,所以激活哪个catch的处理代码是由throw抛出对象决定的。
- catch异常处理代码是调用链中与throw抛出对象类型匹配且离抛出异常位置最近的那一个。
- 抛出的异常对象可以是任意的,被抛出的异常对象会生成一个异常对象的临时拷贝,用于传递给catch捕捉异常,临时对象会在被catch后销毁。
- catch(...)可以捕获任意类型的异常,是避免没有捕获到异常导致程序终止的一个保险措施。
需要注意的是,try与catch使用的 {} 是不能被省略的,此外,throw抛出异常被catch捕获,是按异常对象相匹配的前提下,逐层寻找最近的一个catch进行异常处理的,然后沿着catch后的代码继续运行,如果直到main ()函数都没有寻找到合适的catch,查询将会直接终止。
throw的重新抛出
在某些情况下,当前catch捕获到throw抛出的异常对象并不能在当前进行完全处理,此时可以在catch中二次使用throw再次抛出相同的异常对象,交予更上一层的函数体进行处理。
int Div(int m, int n)
{
if (n == 0)
{
throw string("除数不能为0");//抛出异常会直接跳到对应catch位置,找离得最近的cach,直到mian函数
}
return m / n;
}
void func()
{
int* p = new int;
int a;
int b;
try
{
cin >> a >> b;
*p = Div(a, b);
}
catch (...)
{
delete[] p;
throw;//delete后将异常抛出交予外层函数处理
}
delete[] p;
}
异常规范
对于异常的使用,实际还有着更加规范的用法,用于某一函数体后,可以起到提醒的作用,只不过在实践中因为很多人嫌麻烦,不会去使用这些异常的规范,但我们还是需要了解如何使用。
异常规格说明:throw()
- throw()用于函数后方,( ) 中放入可能抛出的异常类型,可以是一个或多个。
- ()中如果没有放任何类型参数,则表示该函数不会抛出异常。
- noexcept用于函数体后,表示该函数不会抛出异常,与throw ( ) 不带参数的效果一致。
异常安全
异常的抛出并不是在任意地方都可随意使用,对于类中的构造函数、析构函数这种可能涉及到类对象的初始化以及资源的销毁的函数中,最好不要去使用抛出异常。
例如:对于在构造函数中使用抛出异常,就可能会导致类对象并没有完全初始化,这可能会导致问题进一步难以预测;在析构函数中使用抛出异常则有可能导致类对象的资源销毁失败,从而引起资源泄露的问题。
异常的一些优缺点:
异常的使用可以清楚准确的显示出错误的各种信息,帮助更好的确定程序bug的问题出在哪里,此外异常也有着有多第三方库可供我们使用。但异常的使用也会带来一些不利之处,一旦程序运行时出错时,抛异常就会乱跳,会导致程序的调试变得更加困难,此外异常也有着异常安全问题,使用不当可能会导致内存泄露等问题。但总而言之,异常是一个利大于弊的东西,是值的使用的一个工具。