第十九章 异常
19.1 异常的基本概念
遇到错误 抛出异常 捕获异常
异常:是指在程序运行的过程中发生的一些异常事件(如:除0溢出,数组下标越界,所要读取的文件不存在,空指针,内存不足,访问非法内存等等)。(异常是一个类)
c++异常机制相比C语言异常处理的优势?
函数的返回值可以忽略,但异常不可忽略。(忽略异常 程序结束)
整型返回值没有任何语义信息。而异常却包含语义信息,有时你从类名就能体现出来。
19.2 异常的抛出和捕获
19.2.1 异常的抛出和捕获
try
{
throw 异常值;
}
catch(异常类型1 异常值1)
{
处理异常的代码1;
}
catch(异常类型2 异常值2)
{
处理异常的代码2;
}
catch(...)//任何异常都捕获
{
处理异常的代码3;
}
19.2.2 异常抛出和捕获案例
int ret = 0;
try
{
//throw 1;
//throw 'A';
throw 2.14f;
}
catch(int e) //捕获
{
cout<<"char 异常值为:"<<e<<endl;
}
catch(char e)//捕获
{
cout<<"char异常值为:"<<e<<endl;
}
catch(...)//捕获所有异常
{
cout<<"其他异常值为:"<<endl;
}
19.3 栈解旋
异常被抛出后,从进入try块起,到异常被抛掷前,这期间在栈上构造的所有对象,都会被自动析构。析构的顺序与构造的顺序相反,这一过程称为栈的解旋。
try
{
Data ob1;
Data ob2;
Data ob3;
throw 1;//抛出异常后 ob3 ob2 ob1 依次自动释放(栈解旋)
}
19.3.1 栈解旋案例
#include <iostream>
using namespace std;
class Data{
public:
int a;
public:
Data(){}
Data(int a)
{
this->a = a;
cout<<"构造函数"<<a<<endl;
}
~Data()
{
cout<<"析构函数"<<a<<endl;
}
};
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;
}
}
19.4 异常的接口声明
异常的接口声明:描述的是 可以抛出哪些类型的异常值
19.4.1 函数默认 可以抛出任何类型的异常(推荐)
void fun01()
{
//throw 1;
//throw '1';
throw "hello";
}
19.4.2 只能抛出特定类型异常
void fun02() throw(int,char)
{
//throw 1;
//throw '1';
throw "hello";//抛出 不能捕获
}
19.4.3 不能抛出任何异常
void fun02() throw()
{
throw 1;
//throw '1';
//throw "hello";//抛出 不能捕获
}
19.5 异常变量的生命周期
class MyException
{
public:
MyException(){
cout<<"异常变量构造"<<endl;
};
MyException(const MyException & e)
{
cout<<"拷贝构造"<<endl;
}
~MyException()
{
cout<<"异常变量析构"<<endl;
}
};
19.5.1 以普通对象 接异常值
19.5.2 以对象指针 接异常值
19.5.3 对象引用 接异常值(推荐)
19.6 异常的多态
//异常基类
class BaseException{
public:
virtual void printError(){};
};
//空指针异常
class NullPointerException:public BaseException{
public:
virtual void printError(){
cout<<"空指针异常!"<<endl;
}
};
//越界异常
class OutOfRangeException:public BaseException{
public:
virtual void printError(){
cout<<"越界异常!"<<endl;
}
};
void doWork()
{
//throw NullPointerException();
throw OutOfRangeException();
}
int main()
{
try{
doWork();
}
catch (BaseException& ex)//父类引用 可以捕获搭配该父类派生出所有子类的子类
{
ex.printError();
}
}
19.7 c++标准异常
异常名称 | 描述 |
---|---|
exception | 所有标准异常类的父类 |
bad_alloc | 当operator new and operator new[],请求分配内存失败时 |
bad_exception | 这是个特殊的异常,如果函数的异常抛出列表声明了badexception异常,当函数内部抛出了异常抛出列表中没有的异常,这是调用的unexpected函数中若抛出异常,不论什么类型,都会被替换为badexception类型 |
bad_typeid | 使用typeid操作符,操作一个NULL指针,而该指针是带有虚函数的类,这时抛出bad_typeid异常 |
bad_cast | 使用dynamic_cast转换引用失败的时候 |
ios_base::failure | io操作过程出现错误 |
logic_error | 逻辑错误,可以在运行前检测的错误 |
runtime_error | 运行时错误,仅在运行时才可以检测的错误 |
logic_error的子类:
异常名称 | 描述 |
---|---|
length_error | 试图生成一个超出该类型最大长度的对象时,例如vector的resize操作。 |
domain_error | 参数的值域错误,主要用在数学函数中。例如使用一个负值调用只能操作非负数的函数。 |
outo/range | 超出有效范围。 |
invalid_argument | 参数不合适。在标准库中,当利用string对象构造bitset时,而string中的字符不是‘0’或‘1’的时候,抛出该异常。 |
runtime_error的子类
异常名称 | 描述 |
---|---|
range_error | 计算结果超出了有意义的值域范围 |
overflow_error | 算术计算下溢 |
underflow_error | 算术计算上溢 |
invalid_argument | 参数不合适。在标准库中,当利用string对象构造bitset时,而string中的字符不是‘0’或‘1’的时候,抛出该异常。 |
19.8 编写自己的异常
基于标准异常基类 编写自己的异常类。
class NewException:public exception
{
private:
string msg;
public:
NewException(){}
NewException(string msg)
{
this->msg = msg;
}
//重写父类的what
virtual const char* what() const throw()//防止父类在子类前抛出标准异常
{
//将string类转换成char *
return this->msg.c_str();
}
~NewException(){}
};
int main()
{
try
{
throw NewException("自己的异常");
}
catch(exception &e)
{
cout<<e.what()<<endl;
}
}