一、C++异常:
语法错误(编译错误):
比如变量未定义、括号不匹配、关键字拼写错误等等编译器在编译时能发现的错误,这类错误可以及时被编译器发现,而且可以及时知道出错的位置及原因,方便改正。
运行时错误:
比如数组下标越界、系统内存不足等等。这类错误不易被程序员发现,它能通过编译且能进入运行,但运行时会出错,导致程序崩溃。
如我们进程使用一些垃圾软件会闪退,这就是因为他门的程序异常没有处理好,所以遇到预测范围外的错误时,程序就会直接终止。
所以为了解决这种情况,在C++里面就提出了异常处理机制,当一个函数无法处理产生的错误时,就抛出异常,让函数的调用者直接或者间接处理从错误。
二、异常处理的优缺点:
优点:
1.可以清晰的展示出错误原因,不像返回错误码那么模糊
2.许多第三方库使用异常,因此容易与这些结合使用
3.在测试框架里使用比较方便
缺点:
1.会打断执行流,函数有可能不在该返回的地方返回,这样使得代码的管理和调试困难
2.异常安全加大了代码量,需要大量的支持。
三、C++异常处理机制:
代码环境:VS2010
1.异常语法格式
在C++中,异常的抛出和处理主要使用了以下三个关键字:try、 throw 、 catch.其格式如下:
当我们在程序中想抛出一个异常时,可以这样:
#include<iostream>
#include<exception>
using namespace std;
//使用异常处理
int Div(int left,int right){
if(right==0){
throw exception("除数不能为0");
}
return left/right;
}
//未使用异常处理
int UnDiv(int left,int right){
return left/right;
}
当我们想使用这个函数时,需要在函数外部进行异常的捕获:
int main(){
try{
Div(10,20); //合法
Div(10,30); //合法
Div(10,0); //非法,会抛出异常
}catch(exception & e){
cerr<<e.what()<<endl; //打印异常信息
}
return 0;
}
输出结果:
未加入异常处理时:
int main(){
UnDiv(10,20); //合法
UnDiv(10,30); //合法
UnDiv(10,0); //非法,程序奔溃
return 0;
}
输出结果:程序崩溃
2.异常处理流程:
a).异常抛出
下面的例子中,通过throw抛出了一个异常类的实例,这个异常类,可以是任何一个自定义的类,通过实例化传入的参数可以表明发生的错误信息。其实异常就是一个带有异常信息的类而已。
class MyError {
const char* const data;
public:
MyError(const char* const msg = 0):data(msg)
{
//idle
}
};
void do_error() {
throw MyError("something bad happend");
}
int main()
{
do_error();
}
b).异常捕获
C++中通过catch关键字来捕获异常,捕获异常后可以对异常进行处理,这个处理的语句块称为异常处理器。下面是一个简单的捕获异常的例子:
try{
//do something
int left = 5;
int right = 0;
left/right;
throw string("this is exception");
}
catch(const string& e) {
cout << "catch a exception " << e << endl;
}
c).异常匹配
使用自定义的类来异常抛出;
class Base{
public:
Base(string msg):m_msg(msg) {
}
virtual void what(){
cout << m_msg << endl;
}
void test() {
cout << "I am a CBase" << endl;
}
protected:
string m_msg;
};
//派生类,重新实现了虚函数
class CBase : public Base
{
public:
CBase(string msg):Base(msg) {
}
void what() {
cout << "CBase:" << m_msg << endl;
}
};
int main()
{
try {
//do some thing ,like
int left = 5;
int right = 0;
left/right;
//抛出派生类对象
throw CBase("I am a CBase exception");
}
catch(Base& e) { //使用基类可以接收
e.what();
}
getchar();
return 0;
}
输出结果:
四、C++异常处理注意的地方
-
1.代码中抛出异常对象后,会在函数调用链里与该对象类型匹配且离抛出异常处最近的catch匹配(就近原则)。
-
2.异常抛出后,会打乱程序执行流,会释放局部存储对象,因此在调试含异常的代码比较难调试。
-
3.异常抛出的类型必须和catch捕捉的类型必须匹配才可捕获,只有以下三种情况例外:
-
允许非const对象到const对象的转换
- 允许派生类到基类的转换,这一点很重要,因为库里的exception就用到了这条原理。
- 将数组转换为指向数组的指针,将函数转换为指向函数的指针。
-
4.(异常的再次抛出)catch可以将收到的异常处理后再次抛给外层调用链,再由外层catch进一步处理。
-
5.记住!不要在析构函数里和构造函数里抛异常。分别可能会导致对象数据清理不完全,造成内存泄漏。可能会造成对象初始化不完全,在别人使用时可能调用野指针。