异常处理与真正的错误处理有一定的区别,异常处理不但可以对系统做出反应,还可以对人为制造的错误做出反应并处理。
抛出异常
当函数执行到某一函数或方法内部时,程序本身出现了一些异常,但这些异常并不能由系统所捕获,这时就可以创建一个错误信息,再由系统捕获该错误信息并处理。创建错误信息并发送这一过程就是抛出异常。
在C++中,异常的抛出是使用throw关键字来实现,这个关键字的后面可以跟随任何类型的值。
代码示例:
#include "stdafx.h"
#include "iostream.h"
int main(int argc, char* argv[])
{
try
{
throw 1;//抛出异常信息
}
catch(int error)//catch紧随其后,捕获异常信息
{
if(error == 1)
{
cout<<"产生异常"<<endl;
}
}
return 0;
}
---------
产生异常
---------
异常捕获
异常捕获是指当一个异常被抛出时,不一定就在异常抛出的位置来处理这个异常,而是可以在别的地方通过捕获这个异常信息后再进行处理。如果在函数内抛出一个异常(或者函数调用时抛出一个异常),将在异常抛出时退出函数。如果不想在异常抛出时退出函数,可以在函数内创建一个特殊块(try)用于解决实际程序中的问题。
异常处理器紧随try块之后,处理的方法由关键字catch引导。异常处理部分必须直接放在测试块之后,处理器中的第一个参数与异常抛出对象相匹配的函数将捕获该异常信号,然后进入相应的catch语句,执行异常处理程序。
// test.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include "iostream.h"
#include "string.h"
//异常类
class customError
{
private:
int Error_ID;
char ErrorInfor[128];
public:
customError()
{
Error_ID = 1;
strcpy(ErrorInfor,"出现异常!");
}
int GetErrorID(){return Error_ID;}
char* GetErrorInfor(){return ErrorInfor;}
};
int main(int argc, char* argv[])
{
try
{
throw (new customError());
}
catch(customError* error)
{
cout<<"异常ID"<<error->GetErrorID()<<endl;
cout<<"异常信息"<<error->GetErrorInfor()<<endl;
}
return 0;
}
-----------------
异常ID1
异常信息出现异常!
-----------------
有时候并不一定在列出的异常处理中包含所有可能发生的异常类型,C++提供了可以处理任何类型异常的方法,就是在catch后面的括号内添加“...”。
// test.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include "iostream.h"
#include "string.h"
//异常类
class customError
{
private:
int Error_ID;
char ErrorInfor[128];
public:
customError()
{
Error_ID = 1;
strcpy(ErrorInfor,"出现异常!");
}
int GetErrorID(){return Error_ID;}
char* GetErrorInfor(){return ErrorInfor;}
};
int main(int argc, char* argv[])
{
try
{
throw 10;
//throw (new customError());
}
catch(customError* error)
{
cout<<"异常ID"<<error->GetErrorID()<<endl;
cout<<"异常信息"<<error->GetErrorInfor()<<endl;
}
catch(char* error)
{
cout<<"异常信息:"<<error<<endl;
}
catch(...)
{
cout<<"未知异常信息!"<<endl;
}
return 0;
}
--------------
未知异常信息!
--------------
异常匹配
当程序中有异常抛出时,异常处理系统会根据异常处理器的顺序找到最近的异常处理块,并不会搜索更多的异常处理块。异常匹配并不要求异常与异常处理器进行完美匹配,一个对象或一个派生类对象的引用将与基类处理器进行匹配。若抛出的是类对象的指针,则指针会匹配相应的对象类型,但不会自动转换成其他对象的类型。
class CExcept1{};
class CExcept2
{
public:
CExcept2(CExcept1& e){}
};
int main(int argc, char* argv[])
{
try
{
throw CExcept1();
//throw (new customError());
}
catch(CExcept2)
{
printf("进入CExcept2异常处理器!\n");
}
catch(CExcept1)
{
printf("进入CExcept1异常处理器!\n");
}
return 0;
}
-----------------------
进入CExcept1异常处理器!
-----------------------
基类处理器捕获派生类的异常:
class CExcept
{
public:
virtual char* GetError()
{
return "基类处理器";
}
};
class CDerive:public CExcept
{
public:
char* GetError()
{
return "派生类处理器";
}
};
int main(int argc, char* argv[])
{
try
{
throw CDerive();
//throw (new customError());
}
catch(CExcept)
{
cout<<"进入基类处理器 \n";
}
catch(CDerive)
{
cout<<"进入派生类处理器 \n";
}
return 0;
}
--------------
进入基类处理器
--------------
由于异常处理器的第一个是CExcept类,该类是CDerive类的基类,所以进入此异常处理器内部 。
为了正确进入指定的异常处理器,在对异常处理器进行排列时应将派生类排在前面,基类排在后面。
int main(int argc, char* argv[])
{
try
{
throw CDerive();
//throw (new customError());
}
catch(CDerive)
{
cout<<"进入派生类处理器 \n";
}
catch(CExcept)
{
cout<<"进入基类处理器 \n";
}
return 0;
}
----------------
进入派生类处理器
----------------
标准异常
C++标准库的一些异常可以直接应用到程序中,比自定义的要简单。
C++提供的一些标准异常:
namespace std
{
//exception 派生
class logic_error;//逻辑错误,程序运行前可以检测出来
class runtime_error;//运行时错误,仅在程序运行中检测到
//logic_error 派生
class domain_error;//违反了前置条件
class invalid_argument;//指出函数的一个无效参数
class length_error;//指出有一个超过类型的最大可表现值长度的对象的企图
class out_of_range;//参数越界
class bad_cast;//在运行时类型识别中有一个无效的表达式
class bad_typeid;//报告在表达式typeid(*p)中有一个空指针
//runtime_error派生
class range_error;//违反后置条件
class overflow_error;//报告一个算术溢出
class bad_alloc;//存储分配错误
}