概念
程序因硬件或代码编写时考虑不足导出的程序崩溃
硬件 : 不予处理
代码编写考虑不足 : 要处理
分类 :
编译时错误 : 因语法错误导致
运行时错误 : 考虑不足
抛出异常
关键字
throw:抛出
语法:
throw 数据;
如
throw 1;
throw 1.1;
throw 'c';
throw Person();
throw new Person();
...
捕获异常
语法
try
{
可能出现异常的代码
}
catch(数据类型1 变量名1)
{
}
catch(数据类型2 变量名2)
{
}
...
注意
如果try中出现异常代码,其try中剩余代码将不再执行,进入对应的catch中
catch中变量的值就是抛出异常时throw后的数据
catch可以有多个
栈解旋
概念
当try中出现异常,其异常代码之上创建的对象都会被释放
其释放顺序和创建顺序相反
这种情况称为栈解旋
注意:new创建的对象在堆区,无法自动释放
示例1
#include<iostream>
using namespace std;
class Date{
public:
Date(){
cout<<"Date 构造函数"<<endl;
}
~Date(){
cout<<"Date 析构函数"<<endl;
}
};
class Date02{
public:
Date02(){
cout<<"Date02 构造函数"<<endl;
}
~Date02(){
cout<<"Date02 析构函数"<<endl;
}
};
int main(int argc, char const *argv[])
{
try
{
Date *d01=new Date();
Date02 *d02=new Date02();
// Date02 d02;
// Date d01;
throw 1;
}
catch(int e)
{
cout<<"xxxx"<<endl;
}
return 0;
}
上面例子因为在堆区开辟,只能调取构造函数,无法析构
结果:
示例2(将示例1简单进行修改)
#include<iostream>
using namespace std;
class Date{
public:
Date(){
cout<<"Date 构造函数"<<endl;
}
~Date(){
cout<<"Date 析构函数"<<endl;
}
};
class Date02{
public:
Date02(){
cout<<"Date02 构造函数"<<endl;
}
~Date02(){
cout<<"Date02 析构函数"<<endl;
}
};
int main(int argc, char const *argv[])
{
try
{
// Date *d01=new Date();
// Date02 *d02=new Date02();
Date02 d02;
Date d01;
throw 1;
}
catch(int e)
{
cout<<"xxxx"<<endl;
}
return 0;
}
此时try在栈中构造函数,可以析构
结果:
异常的接口声明
语法
返回值类型 函数名(形参列表)throw(可能抛出的异常类型1,可能抛出的
异常类型2,...)
{
函数体
}
注意
如果throw()说明当前函数没有异常
throw()==noexcept关键字
示例
#include<iostream>
using namespace std;
//此时在VSCode会显示红色,但语法没有问题
void myDiv(int x,int y)throw(int,char)
{
if(y==0)
{
throw 1;
}
cout<<x/y<<endl;
}
int main(int argc, char const *argv[])
{
try
{
myDiv(10,0);
}
catch(int e)
{
cout<<"别理我"<<endl;
}
catch(char e)
{
cout<<"脑仁疼"<<endl;
}
return 0;
}
得到的结果为:别理我
异常对象的生命周期
情况1:传递异常对象
传递异常对象,此时会触发拷贝构造,形成一个新的异常对象,那么就得销毁这两个对象
示例
#include<iostream>
using namespace std;
class MyException
{
public:
MyException()
{
cout<<"构造函数被调用"<<endl;
}
MyException(const MyException& e)
{
cout<<"拷贝构造被调用"<<endl;
}
~MyException(){
cout<<"析构函数被调用"<<endl;
}
};
int main(int argc, char const *argv[])
{
try
{
//MyException():创建了一个MyException的一个对象,该对象没有对象名,称为匿名对象
throw MyException();
}
catch(MyException e)
{
}
return 0;
}
结果
情况2:传递异常对象指针
传递异常对象,创建一次,但是不销毁
示例
#include<iostream>
using namespace std;
class MyException
{
public:
MyException(){
cout<<"构造函数被调用"<<endl;
}
MyException(const MyException& e)
{
cout<<"拷贝函数被调用"<<endl;
}
~MyException(){
cout<<"析构函数被调用"<<endl;
}
};
int main(int argc, char const *argv[])
{
try
{
//传递的是指针
throw new MyException();
}
catch(MyException* e)
{
}
return 0;
}
结果
情况3:传递异常对象引用(建议使用)
传递异常对象引用,只会创建一次,而且可以自动销毁
示例
#include<iostream>
using namespace std;
class MyException
{
public:
MyException()
{
cout<<"构造函数被调用"<<endl;
}
MyException(const MyException& e)
{
cout<<"拷贝构造被调用"<<endl;
}
~MyException()
{
cout<<"析构函数被调用"<<endl;
}
};
int main(int argc, char const *argv[])
{
try
{
//传递的是异常对象的引用
throw MyException();
}
catch(MyException& e)
{
}
return 0;
}
结果
异常的多态
注意
1.抛出子类异常,可以被父类异常类型接收
2.抛出子类异常,catch中父类异常类型与子类异常类型,此时按代码书写顺序接收,
建议先子后父
示例
#include<iostream>
using namespace std;
class MyException{};
class NullException:public MyException{};
int main(int argc, char const *argv[])
{
try
{
throw NullException();
}
catch(NullException& e)
{
cout<<"NullException"<<endl;
}
catch(MyException& e)
{
cout<<"MyException"<<endl;
}
return 0;
}
结果
标准异常库
概述
由 C++ 提供的一套异常相关的类
自定义异常类
步骤
1, 定义一个类使其继承与 exception 或其子类
2, 定义一个变量记录异常信息
3, 定义该类的构造函数 , 拷贝构造 , 析构函数等
4, 重写 what 函数
const char* what() const noexcept
{
return 步骤 2 定义的变量 ;
}
注意
编译使用需加-std=c++11