异常
在我们之前学习的时候,C语言遇到内存泄漏或者内存越界的时候,要么程序直接就崩了,给一个错误码,写的好一点也就是给个assert,但是程序仍会终止,要么就根本发现不来,C++中我们也常说,当越界时会抛异常
异常的概念
异常是是C++处理程序错误的一种方式,当程序发生错误的时候,就会抛出异常,这个异常可以是程序自发给出的,也有可能是我们手动写的throw,当抛出异常之后,我们可以在别的地方捕获异常,catch到就可以对异常进行提示和处理
这样做有一个好处是,当程序出错时不一定会完全终止程序,而是可以有一个补救措施
一般有三个关键字
throw:这个就是扔出去的意思,扔出一个异常,所谓的异常其实就是一个类
catch:这个是捕获异常,当捕获到了这个异常(类)的时候,需要执行的语句
try:这个是用于执行可能会出错的代码,在代码中出错的语句抛出就可以被catch到
例如
double func(int a, int b)
{
if(b==0)
throw "division by zero";
return 1.0*a/b;
}
int main()
{
try{
func();
}
catch(string s)
{
cout<<s<<endl;
}
return 0;
}
需要注意的是,当抛出异常时,也就是执行到throw语句的时候,会直接跳出到catch的语句,throw后面的语句就不会被执行,如果抛出的异常没有被捕获,那么程序就会终止,显示未经处理的异常
异常的抛出与匹配
- throw和catch的类型要保持一致
因为我们说throw的其实是一个类,那么catch的类也要保持一致
而且auto这里也用不成
- try可以嵌套
try里面可以是一个函数,函数里面再继续try也没问题
-
throw会飞到与之最近的且匹配的catch
-
catch(...)
可以捕获所有异常
这里是直接可以写到代码里的,类似于switch的default
- 基类可以接受抛出的子类对象
这点就非常实用了,当一个项目非常大的时候,重复写很多类其实没有必要而且加重负担,就不如写一个异常类,然后每多一种异常的类型,我们对其进行继承即可
异常的重新抛出
这里主要是为了解决几个问题
一是当一次捕获异常并不能完全处理所有问题的时候
例如在普通函数中出现了问题,捕获异常之后对异常进行第一步的处理,比如说释放开出来的空间,然后我们可以继续抛,抛到主函数里,对所有的异常信息进行统一打印
C++标准库的异常体系
C++给了一系列的标准体系,他们是以父类和子类的层次组织起来的,基本内容如下,但其实还是自己写一个异常体系会更好一点
异常的注意事项
总的来说异常还是一件好事情,方便我们能快速的处理问题,尤其是对于面向对象语言,基本上都是异常处理错误
可以在函数的后面加上throw(类)表示这个函数会抛出这些类型的异常
void func() throw(A,B);
如果throw这个括号里面是空的,说明这个函数不抛异常
如果不写就是有可能抛出所有类型的异常
在C++11中可以使用noexcept表示不会抛异常
异常不太友好的地方是执行流乱跳,我们虽然可以很好的知道错了,是哪类错误,但是还是比较难以进行调试的
而且C++不会自动进行内存回收,我们还是需要自己手动管理,因此有异常之后就很容易导致内存泄露,后面我们讲智能指针就会好很多
异常还是需要规范使用,不能随意抛异常,一抛一时爽,调试火葬场,因此这就要求我们需要灵活使用继承,并且使用函数尽量避免在同一个作用域导致内存泄漏