1概念
1.1基本思想:
- 让一个函数发现自己无法处理的的错误抛出一个异常 然后将它的调用者能够处理这个函数,将问题检测和问题处理分离
- 异常处理就是:
处理程序中的错误 所谓错误是指在程序运行的过程中发生的一些异常事件(IO溢出,数组下标越界 所读取的文件不存在,内存不足)
1.2特点
- 不干扰正常的返回值。
- 必须处理异常。
- 只要抛出异常,异常后的代码不再执行。
- 异常的所抛出与经过的栈都会销毁。
1.3语法
抛出异常
throw 表达式;
捕获并处理异常
try {
} catch (类型名 [形参名]) {
} 、
1.4C语言措施
1.5示例
#include<iostream>
using namespace std;
int myDivide(int a,int b){
if(b==0){
throw -1;
throw 1.23;
throw 'a';
}
return a/b;
}
void test(){
int a=10;
int b=-0;
try{
myDivide(a,b);
}
catch(int){
cout<<"int 类型的异常"<<endl;
throw 1.5;
}
catch(double){
cout<<"double 类型的异常"<<endl;
}
catch(...){
cout<<"其他 类型的异常"<<endl;
}
}
int main(){
try{
test();
}
catch(int){
cout<<"int 类型的异常"<<endl;
}
cout<<"main return" <<endl;
}
2其他知识点
2.1自定义异常类
#include<iostream>
using namespace std;
class MyException{
public:
void printfError(){
cout<<"我自己的异常类的错误"<<endl;
}
};
class Person{
public:
Person(){
cout<<"pereson 的构造函数"<<endl;
}
~Person(){
cout<<"pereson 的系造函数"<<endl;
}
};
int myDivide(int a,int b){
if(b==0){
Person p1;
Person p2;
cout<<"aaaaa"<<endl;
throw MyException();
}
return a/b;
}
void test(){
int a=10;
int b=-0;
try{
myDivide(a,b);
}
catch(MyException e){
e.printfError();
}
catch(...){
cout<<"其他 类型的异常"<<endl;
}
}
2.2栈解璇
//栈解璇 从try代码块开始,到throw抛出异常前 所有栈上的对象都被释放掉
2.3异常接口说明
返回值类型 函数() throw(异常列表);
- 指定函数可以抛出何种异常,如果没有throw(异常列表)默认可以抛出所有异常。异常列表为空throw()。指定函数不抛出异常,指定什么异常就必须抛出什么异常。
2.4异常变量的声明周期
#include<iostream>
using namespace std;
class MyException{
public:
MyException(){
cout<<"构造异常类"<<endl;
}
MyException(const MyException& e){
cout<<"拷贝构造异常类"<<endl;
}
void printfError(){
cout<<"我自己的异常类的错误"<<endl;
}
~MyException(){
cout<<"希构异常类"<<endl;
}
};
int myDivide(int a,int b){
if(b==0){
throw MyException();
}
return a/b;
}
void test(){
int a=10;
int b=0;
try{
myDivide(a,b);
}
catch(MyException& e){
e.printfError();
}
catch(...){
cout<<"其他 类型的异常"<<endl;
}
}
int main(){
MyException e=test();
cout<<"main return" <<endl;
}
3标准异常类
3.1特点
- 1:所有的体系函数中都有构造函数 复制构造函数 和复制运算符重载
- 2:logic_error,runtime_error及其子类,他们的构造函数接受一个string类型的参数的形式参数 用于异常的描述
- 3:所有的异常类都有what()方法 返回const char *类型的值 描述异常信息
3.2exception派生
异常类 | 作用 |
---|
logic_error | 逻辑错误,在程序运行前可以检测出来 |
runtime_error | 运行时错误,仅在程序运行中检测到 |
3.2.1逻辑异常logic_error派生
异常类 | 作用 |
---|
domain_error | 违反了前置条件 |
invalid_argument | 指出函数的一个无效参数 |
length_error | 指出有一个超过类型size_t的最大可表现值长度的对象的企图 |
out_of_range | 参数越界 |
bad_cast | 在运行时类型识别中有一个无效的dynamic_cast表达式 |
bad_typeid | 报告在表达试typeid(*p)中有一个空指针p |
3.2.2运行时runtime_error派生
异常类 | 作用 |
---|
range_error | 违反后置条件 |
bad_alloc | 存储分配错误 |
3.3编写自己的异常类
#include<iostream>
#include<stdexcept>
using namespace std;
class MyoutofRange:public exception{
public:
MyoutofRange(char * errorinfo){
this->my_errorinfo=string(errorinfo);
}
MyoutofRange(string errorinfo){
this->my_errorinfo=errorinfo;
}
virtual const char* what( )const{
return this->my_errorinfo->c_str();
}
virtual ~MyoutofRange(){
}
string my_errorinfo;
};
class Person{
public:
Person(int age){
if(age<0||age>150){
throw MyoutofRange("我的异常类--<F9>年龄必须在0——150");
}
m_age=age;
}
int m_age;
};
void test1(){
try{
Person p1(152);
}
catch(exception& e){
cout<<e.what()<<endl;
}
}
int main(){
test1();
cout<<"main return" <<endl;
}
4构造函数、析构函数的异常处理
- 构造函数可以抛出异常,此时不会调用析构函数,所以如果抛出异常前,申请了资源,需要自己释放。
- C++标准指明析构函数不能、也不应该抛出异常。
- C++标准规定,构造函数失败,析构函数不会执行。就是说在构造函数抛出异常前分配的资源将无法释放。
如果析构函数抛出异常,则异常点之后的程序不会执行,如果析构函数在异常点之后执行了某些必要的动作比如释放某些资源,则这些动作不会执行,会造成诸如资源泄漏的问题。
通常异常发生时,c++的机制会调用已经构造对象的析构函数来释放资源,此时若析构函数本身也抛出异常,则前一个异常尚未处理,又有新的异常,会造成程序崩溃的问题。