C++异常处理
一、异常处理
-
程序的错误大致分三种:语法错误、逻辑错误、运行时错误
-
运行时错误发生在程序运行期间发生的问题:除零、内存分配失败、非法访问内存、文件不存在、数组越界
-
C++的异常处理机制就是为了解决运行时错误
-
C语言中运行时错误如果不管,系统会执行默认操作,可能会让程序终止产生崩溃,也可能不终止,但是运行结果不正确
-
C++提供的异常处理机制,能够捕捉到运行时错误,至少提供了告诉调用者发生了什么事情导致终止的方式,然后再终止
二、如何抛异常
throw 数据;
-
数据可以是任意类型
-
不要抛出局部变量的地址,因为该地址有可能被释放
-
抛出的数据不是直接传递给捕获的变量,而是先创建一个匿名对象存储该数据,然后传递该匿名对象
三、如何捕获异常
try{
//可能会产生异常的代码或函数调用
}catch(类型1& 变量名){
//进行异常处理1
}catch(类型2& 变量名){
//进行异常处理2
}···
- 如果使用 ’类型 变量名 ‘ 方式捕获异常,会对匿名对匿名对象再拷贝一次,浪费资源时间,所以一般使用引用获取该匿名对象,可以减少一次拷贝
四、异常说明(异常规范)
返回值 函数名(形参列表)[异常说明throw (类型名1,类型名2)]
{
}
例如:
void func(void)throw(类型1,类型2)
void func(void)throw() //表示不抛异常
-
异常说明相当于该函数的限制或承诺,只抛出说明过的异常类型,如果抛出说明外的类型,可以抛出,但是不能接住
-
不同编译器对异常说明的实现不同,有的听从,有的不听从
-
异常说明是C++98的一项功能,C++11后就抛弃了,不建议使用
-
C++11中使用 void func(int x) noexpect 替代
五、标准异常
-
C++已经定义好的异常类,当对应的异常发生时,会自动地抛出定义好的对应的异常类对象
-
std::exception 所有标准异常类的父类,能够捕获所有的标准异常
-
std::bad_alloc new分配内存失败时抛出的异常,std::bad_array_new_length 是它的子类 new分配内存数量有误会抛出
-
std::bad_cast 该异常可以通过 dynamic_cast 抛出
需要#include < typeinfo >
-
std::bad_typeid 该异常可以通过typeid 抛出
-
当获取具有多态属性的类型指针解引用的类型时,如果不能确定解引用后是哪个类型时,会抛出该异常
Base* b = new Base; Base* b1 = new Son; Base* b2 = NULL; typeid(*b); //类型 Base typeid(*b1); //类型 Son typeid(*b2); //抛异常
-
六、自定义异常类
通过设计一个继承了exception的异常类,可以个性化地抛出想要的异常
#define ZZERROR(...) ZZError(__TIME__,__FILE__,__func__,__LINE__,__VA_ARGS__)
class ZZError : public exception
{
string time;
string file;
string func;
size_t line;
string error;
public:
ZZError(const string& time,const string& file,const string& func,size_t line,const string& error):time(time),file(file),func(func),line(line),error(error){}
~ZZError(void){}
const string& what(void)
{
return error;
}
friend ostream& operator<<(ostream& os,const ZZError& err)
{
return os << "time:" << err.time << " file:" << err.file <<" func:" << err.func << " line:" << err.line << " error:" << err.error;
}
};
七、使用异常需要注意的问题
-
不要抛出局部变量、对象的地址,而是抛出变量、对象本身
-
建议使用引用的方式来捕获异常,减少一次拷贝
-
不要在构造函数、析构函数中抛异常
-
在捕获异常时,先捕获子类类型,再捕获父类类型