异常的基本概念
遇到错误 抛出异常 捕获异常
异常:是指在程序运行的过程中发生的一些问题(如:除0溢出,数组下标越界,要读取的文件不存在,空指针,内存不足,访问非法内存等)。
异常是一个类
异常处理不适用于处理异步事件相关的错误的:磁盘去写结束、网络信息到达等
异常处理机制还适用于处理程序与其他软件元素:成员函数、构造函数、析构函数、类交互时出现的问题。
C++异常机制与C语言异常处理的优势:
1.函数的返回值可以忽略,但异常不可以忽略。(忽略异常,程序结束)
2.整型返回值没有任何语义信息。而异常却包含语义信息,有时从类名就可以体现出来
基本思想
异常的抛出与捕获
异常的抛出捕获
try
{ //抛出异常,可能是一小段代码,更可能的是调用别的函数
throw 异常值;
}
catch(异常类型1 异常值1) //捕获异常
{
处理异常的代码1;
}
catch(异常类型2 异常值2)
{
处理异常的代码2;
}
catch(...)//捕获所有异常
{
处理异常的代码3;
}
如果匹配的处理器没有被找到,则库函数ternubate
将会被自动调用,其默认是调用abort
来终止程序
案例
#include<iostream>
using namespace std;
int divide(int x, int y) {
if (y == 0)
throw x;
return x / y;
}
int main() {
try
{
cout << "5/2=" << divide(5, 2) << endl;
cout << "8/0=" << divide(8, 0) << endl;
cout << "7/1=" << divide(7, 1) << endl;
}
catch (int e)
{
cout << e << "is divide by zero!" << endl;
}
cout << "That is ok." << endl;
return 0;
}
第一次调用时为divide(5,2)
调用正确,第二次调用时为divide(8,0)
,从发生异常的这一点之后都不会执行了,所以直接跳到throw x;
之后又跳到catch(int e)
继续执行
栈解旋
异常被抛出后,从进入try块起,到异常被抛弃前,这期间在线上构造的所有对象,都会被自动析构。析构的顺序与构造的顺序相反,这一过程称为栈的解旋。
案例
#include<iostream>
using namespace std;
class Data
{
public:
Data(){}
Data(int a)
{
this->a = a;
cout << "构造函数" << a << endl;
}
~Data() {
cout << "析构函数" << a << endl;
}
private:
int a;
};
int main()
{
int ret = 0;
try
{
Data ob1(10);
Data ob2(20);
Data ob3(30);
throw 1;
}
catch (int)//捕获
{
cout << "int的异常值:" << endl;
}
catch (char)//捕获
{
cout << "char的异常值:" << endl;
}
catch (...)//捕获
{
cout << "其他异常值" << endl;
}
}
先构造 后析构结束时候再捕获异常,这个过程叫做栈解旋
异常声明(异常规范)
一个函数显式地声明可能抛出的异常,有利于函数的调用者为异常做好准备
函数默认:可以抛出任何类型的异常
void fun01()
{
//throw 1;
//throw '1';
throw "hello";
}
只能抛出特定类型异常
void fun02()throw(int,char)
{
//throw 1;
//throw '1';
throw "hello"; //抛出,不能捕获 只能捕获int,char类型
}
不抛出任何异常
void fun01()throw() //或void fun01() noexcept;
{
throw 1;
//throw '1';
//throw "hello";
}
- 对于明确不抛出异常的函数,C++推荐使用
noexcept
说明修饰 - 方式:返回值类型
func(形参列表) noexcept;
- 异常处理使编译和运行时都有额外的开销,省去异常处理可优化加速调用
- 需爆出该函数内部调用函数和定义语句均不会抛出异常的一致性
- 配套有
noexcept
运算符,可以判断函数是否使用了noexcept
说明
void f() noexcept;
noexcept(f());//返回true,因为f有noexcept说明
异常处理中的构造与析构
自动的析构
- 找到一个匹配的catch异常处理后
- 初始化异常参数
- 将从对应的try块开始到异常被抛掷处之间构造(且尚未析构)的所有自动对象进行析构
- 从最后一个catch处理之后开始恢复执行。
#include<iostream>
#include<string>
using namespace std;
class MyException {
public:
MyException(const string &message):message(message){}
~MyException() {}
const string &getMessage() const { return message; }
private:
string message;
};
class Demo {
public:
Demo() { cout << "Constructor of Demo" << endl; }
~Demo()
{
cout << "Destructor of Demo" << endl;
}
};
void func() throw(MyException) {
Demo d;
cout << "Throw MyException is func()" << endl;
throw MyException("exception thrown by func()");
}
int main() {
cout << "in main function" << endl;
try
{
func();
}
catch (MyException& e)
{
cout << "Caught an exception:" << e.getMessage() << endl;
}
cout << "Resume the execution of main()" << endl;
return 0;
}