10,异常处理
1,定义:
异常处理就是处理程序中的错误;
2,基本思想:
让一个函数在发现了自己无法处理的错误时,抛出(throw)一个异常,然后它的(直接或间接)调用者可以吹这个问题;------即将问题检测和问题处理分离;
3,异常基本语法
1,抛出异常使用throw;
2,捕获异常使用try{}catch(){};
throw:当问题出现时,程序会抛出一个异常;
catch:在你想要处理问题的地方,通过异常处理程序捕获异常;
try:try块中的代码标识将被激活的特定异常,它后面通常跟着一个或多个catch块;
noexcept:用于声明函数不抛出异常,如果函数抛出了异常,则直接中断,不能被捕获;
3,语法:
try
{
//保护代码;
}catch(ExceptionName e1)//ExceptionName是设置抛出异常时,数据的类型;
{
//catch块
}catch(ExceptionName e2)
{
...
}
4,捕获异常时的注意事项:
1,catch的匹配过程是找最先匹配的,从上到下;
2,catch的匹配过程中,对类型的要求比较严格,不允许标准算术转换和类类型的转换;
(类类型的转换包括两种:通过构造函数的隐式类型转换,和通过转换操作符的类型转换);
案例:
void n()
{
int res = 0;
if (res==0)
{
/*throw "除数不能为0";*///const char*类型;
throw 999;//int 类型;
}
int nnm = 5 / res;
}
try
{
n();
}
catch (const char* n)
{
cout << "捕获到了char*" <<n<< endl;//输出 捕获到了char*除数不能为0
}
catch (int m)
{
cout << "捕获到了int" <<m<< endl;//输出 捕获到了int999
}
5,异常之栈解旋
异常被抛出后,从进入try块起,到异常被抛出前,这期间在栈上构造的所有对象,都会被自动析构;
析构的顺序和构造的顺序相反,这一过程称为栈的解旋;
案例:
class Maker
{
public:
Maker() { cout << "构造函数" << endl; }
Maker(const Maker& other) { cout << "拷贝构造函数" << endl; }
~Maker() { cout << "析构函数" << endl; }
};
void fff()
{
Maker m;//释放
//int *p=new int[9];//这个不会自动释放
throw 999;
}
int main()
{
try
{
fff();
}
catch (int d)
{
//函数中的局部变量会自动释放;
}
//输出构造函数 析构函数
return 0;
}
6,C++标准的异常
C++提供了一系列标准的异常,定义在中,我们可以在程序中使用这些标准的异常,它们是以父子类层次结构组织起来的;
每个类所在的头文件在图下方标识出来:
标准异常类的成员:
1,在上述继承体系中,每个类都有提供了构造函数,复制构造函数,和赋值操作符重载;
2,logic_error类及其子类,runtime_error类及其子类,它们的构造函数是接受一个string类型的形式参数,用于异常信息的描述;
3,所有的异常类都有一个what()方法,返回const char* 类型(C风格字符串)的值,描述异常信息;
说明:
异常 | 描述 |
---|---|
std::exception | 该异常是所有标准C++异常的父类; |
std::bad_alloc | 该异常可以通过new抛出; |
std::bad_cast | 该异常可以通过dynamic_cast抛出; |
std::bad_exception | 这在处理C++程序中无法预期的异常时非常有用; |
std::bad_typeid | 该异常可以通过typeid抛出; |
std::logic_error | 理论上可以通过读取代码来检测到的异常; |
std::domain_error | 当使用了一个无效的数学域时,会抛出该异常; |
std::invaild_argument | 当使用了无效的参数时,会抛出该异常; |
std::length_error | 当创建了太长的std::string时,会抛出该异常; |
std::out_of_range | 该异常可以通过方法抛出,例如:std::vector和std::bitset<>::operator[] (); |
std::runtime_error | 理论上不可以通过读取代码来检测到的异常; |
std::overflow_error | 当发生数学上溢时,会抛出该异常; |
std::range_error | 当尝试存储超出范围的值时,会抛出该异常; |
std::underflow_error | 当发生数学下溢时,会抛出该异常; |
7,编写自己的异常类
class out_Range:public exception//继承父类异常
{
public:
explicit out_Range(const string&_Msg):exception(_Msg.c_str()){}//第一种方法:
explicit out_Range(const char*_Msg):exception(_Msg){}//第二种方法:
}
...
void fun()
{
throw out_Range("越界了");
}
catch(out_Range& e)
{
cout<<e.what()<<endl; //输出 越界了
}