C++异常(exception)
所谓的异常就是不正常,指在程序运行的过程中,由于系统条件、操作不当等原因所引起的程序运行错误
常见的异常:除零错误、指针访问受保护的空间、数组越界、内存分配失败、打开的文件不错在…
在C++中,新增加了一种错误(异常)处理方式
错误一般分为两种:编译时错误、运行时错误
编译时错误:在程序编译阶段就能够发现的错误
运行时错误:编译能够通过,在运行的时候会有一些错误产生
为了提高系统的健壮性,加强容错能力,在编程过程中,这些错误(异常)必须要处理,防止系统崩溃
我们前面写过的代码都是在哪里发现问题就在哪里解决问题,这种处理方式就是分散式处理
将错误(异常)分散在各处进行处理,不利于代码的维护,尤其是对于不同的地方发生相同的错误(异常),
或者多个地方的错误(异常)处理方式一样,都需要编写相同的代码来进行处理,这也是一种代码的冗余和重复
如果能够在发生各种异常的时候,让程序都自动执行同一个地方的代码,
这个地方的代码能够对所有的异常进行集中的处理,则程序会更容易编写和维护
所以,在C++中引入了异常处理的机制:
基本思想:
函数A在执行的过程中,如果产生了异常,在该函数中不处理异常,而是将该异常抛出,即该函数只"抛出一个异常"
该异常会抛给A的调用者B
抛出异常以后,会导致函数A终止执行,类似于return,
在这种情况下,函数B可以对A的异常进行处理,也可以当作没看见(不处理)
如果函数B对异常置之不理,这个异常就会继续抛给函数B的调用者C,C有两种处理方式:对异常进行处理或者置之不理…
如果一层层的函数都不处理异常,异常最终会抛给最外层的main函数,
main函数应该要处理这个异常,如果不处理,那么这个异常就会抛给main函数的调用者(操作系统)
操作系统就会立即终止(kill)main函数
C++处理异常的进制由三部分组成:
1.抛出异常(throw)(类似于return)
2.捕获异常(try-catch)(类似于if-switch)
3.处理异常
因此,C++引入了throw/try/catch关键字用来处理异常
通常将一组可能会产生异常的代码放到try所保护的代码块内,
如果产生了异常,就由try后面的catch结构来接受异常并处理异常
如果try内没有产生异常,则catch不会被执行,代码正常运行
异常处理结构:
try
{
//可能会出现异常的代码
}catch(类型名1 [形参名]) //[]括起来表示形参可有可无,如果需要使用异常值,就可以设置形参,如果不需要就可以不设置
{
//异常处理方式1
}catch(类型名2 [形参名])
{
//异常处理方式2
}catch(类型名3 [形参名])
{
//异常处理方式3
}
...
catch(...)
{
//表示不确定的异常类型,这里可以捕获其他类型的异常
}
异常处理机制,能够实现发现问题和解决问题分离,是一种思想:
在异常处理过程中,由函数内部来抛出问题(异常值、对象),问题会抛给调用该函数的调用者,
通过抛出的这个问题,实际上完成了两个部分的通信,通信的内容为:出现了什么问题,怎么解决!
抛出的异常值可以是任意类型,包括类类型
在捕获异常值时,catch是通过异常值的类型来进行匹配,所以如果不需要保存(接收)异常值,可以省略形参
异常值在传递到catch前,都会创建一个"临时对象",类似于return
C++标准库中定义了一组异常类型,用于报告标准库中的函数常见的问题,
这些异常类型可以直接在程序中使用(已经定义好了)
常见的异常类型有以下:
exception (异常,是所有异常类的基类)
runtime_error 运行时异常,没有默认构造函数,必须要提供参数
out_of_range 范围越界(数组越界),没有默认构造函数,必须要提供参数
bad_alloc 分配动态内存失败,只有默认构造函数,不需要提供参数
当new失败的时候,会自动抛出该异常
…
有的异常类型的对象在构造的时候需要提供参数(解释性的字符串),有的不需要提供参数
在所有的异常类中只有成员函数:what,该函数没有参数
作用是返回一个C语言风格的字符串,用来提供异常的信息
如:
抛出异常:
throw runtime_error(“运行时错误!”);
throw out_of_range(“范围越界!”);
throw bad_alloc();
捕获异常:
catch(runtime_error &arg)
{
cout << “捕获到一个runtime_error类型的异常值” << endl;
cout << arg.what() << endl;
}
catch(out_of_range &arg)
{
cout << “捕获到一个out_of_range类型的异常值” << endl;
cout << arg.what() << endl;
}
catch(bad_alloc &arg)
{
cout << “捕获到一个bad_alloc类型的异常值” << endl;
cout << arg.what() << endl; //会打印std::bad_alloc
}
注意:
构造函数中可以抛出异常!!!
普通的成员函数中也可以抛出异常
析构函数中不能抛出异常!!!
析构函数默认是 noexcept 的
如果一个函数绝对不会抛出异常,可以在函数的参数列表后面加 noexcept 进行说明
noexcept是一种承诺,说明该函数不会抛出异常
如果一个函数用 noexcept 说明了,但是在函数内部又抛出了异常,
那么在编译时会报警告
一旦调用该函数,运行时系统会直接终止该程序!!!
异常测试代码
#include <iostream>
using namespace std;
class Test
{
};
//小孩独自去上学
void gotoSchool()
{
int step = 0; //上学的步骤
bool isOk = true; //小孩的状况
//经过第一关
cout << "经过女儿国" << endl;
if (!isOk)
{
cout << "被女儿国国王抓走了" << endl;
throw "被女儿国国王抓走了";
}
//经过第二关
cout << "路过龙虎山" << endl;
if (!isOk)
{
cout << "被老虎吃了" << endl;
throw 9999;
}
//经过第三关
cout << "通过流沙河" << endl;
if (!isOk)
{
cout << "成了沙和尚的小点心" << endl;
throw 'g';
}
//经过第四关
cout << "通过红绿灯路口" << endl;
if (!isOk)
{
cout << "被车撞了" << endl;
throw 3.14f;
}
//可以抛出任意类型的数据,包括自定义类型
cout << "Test" << endl;
if (!isOk)
{
cout << "抛出自定义类型的数据" << endl;
Test ts;
throw ts;
}
//抛出标准库中的异常类型的对象
cout << "运行时错误" << endl;
if (!isOk)
{
cout << "抛出标准库中的运行时错误的对象" << endl;
throw runtime_error("运行时错误!");
}
cout << "范围越界" << endl;
if (!isOk)
{
cout << "抛出标准库中的范围越界的对象" << endl;
int array[3];
int i = 4;
if (i > 2)
{
throw out_of_range("范围越界!");
}
}
cout << "分配内存失败" << endl;
isOk = false;
if (!isOk)
{
cout << "抛出标准库中的分配内存失败的对象" << endl;
throw bad_alloc();
}
cout << "成功抵达学校!" << endl;
}
int main()
{
cout << "清早起床了" << endl;
cout << "送小孩去上学" << endl;
cout << "朋友喊打麻将,三缺一" << endl;
cout << "让小孩自己去上学" << endl;
try
{
gotoSchool();
cout << "呦呦呦,煎饼果子来一套!" << endl;
}
catch(int arg)
{
cout << "捕获到一个int型的异常值" << endl;
cout << arg << endl;
}
catch(char arg)
{
cout << "捕获到一个char型的异常值" << endl;
cout << arg << endl;
}
catch(const char *arg)
{
cout << "捕获到一个字符串类型的异常值" << endl;
cout << arg << endl;
}
catch(float arg)
{
cout << "捕获到一个float型的异常值" << endl;
cout << arg << endl;
}
catch(Test arg)
{
cout << "捕获到一个Test类型的异常值" << endl;
}
catch(runtime_error &arg)
{
cout << "捕获到一个runtime_error类型的异常值" << endl;
cout << arg.what() << endl;
}
catch(out_of_range &arg)
{
cout << "捕获到一个out_of_range类型的异常值" << endl;
cout << arg.what() << endl;
}
catch(bad_alloc &arg)
{
cout << "捕获到一个bad_alloc类型的异常值" << endl;
cout << arg.what() << endl;
}
catch(...)
{
cout << "捕获到一个未知类型的异常值" << endl;
}
cout << "结束" << endl;
}