您的位置:首页 > 编程语言 > C语言/C++

【c++编程思想】异常处理

2012-09-12 10:19 211 查看
c语言中常用的异常处理方式:

1.在函数中返回错误信息,耦合度极高
2.使用标准库中的信号处理系统,signal()能够与推断事件发生时出现了什么情况,和函数raise产生一个事件来实现。耦合度也很高,可能和其他信号冲突
3.非局部跳转函数setjmp,可以在程序中保存一个已知的无错误状态,一旦发生错误就可以使用 longjmp 返回到该状态中。

#include<iostream>
#include<csetjmp>
using namespace std;

class rainbow
{
public:rainbow(){
cout<<"rainbow()"<<endl;
}
~rainbow(){
cout<<"~rainbow"<<endl;
}
};

jmp_buf kansas;

void oz()
{
rainbow rb;
for(int i=0;i<3;i++)
{
cout<<"no place !!"<<endl;
}
longjmp(kansas, 47);
}

int main()
{
if(setjmp(kansas) == 0)
{
cout<<"tonado...."<<endl;
oz();
}
else
{
cout<<"ooooooooooo"<<endl;
}
}


函数setjmp的行为很特别,如果直接调用它,它便会将所有与当前处理器相关的状态信息保存到jmp_buf中去并返回0,和普通函数一样。
但是当使用同一个jmp_buf调用longjmp时,函数返回就好像刚从setjmp中返回一样,这一次返回值是调用longjmp时所用的第二个参数,因此可以通过这个值来判断实际从哪里返回。
但是c++中使用这种方式的问题在于它无法识别对象,自动调用析构函数。而此时程序的行为是不确定的。

c++形式的异常处理

throw将会导致一系列动作的发生:

创建程序所抛出的对象的一个拷贝,然后包含throw表达式的函数返回了这个对象。
返回一个值,离开函数或者作用域。
异常发生之前创造的局部对象被销毁,这种对局部对象的处理被称为“栈反解”。

#include<iostream>
#include<csetjmp>
using namespace std;

class rainbow
{
public:rainbow(){
cout<<"rainbow()"<<endl;
}
~rainbow(){
cout<<"~rainbow"<<endl;
}
};

void oz()
{
rainbow rb;
for(int i=0;i<3;i++)
{
cout<<"no place !!"<<endl;
}
throw 47;
}

int main()
{
try
{
cout<<"tonado...."<<endl;
oz();
}
catch(int a)
{
cout<<"ooooooooooo"<<" "<<a<<endl;
}
}


捕获所有异常
catch(...){
//do sth
//throw
}
用省略号就可以捕获所有异常,最好将他放在异常处理列表的最后,且不允许接受任何参数,因此一般用于清理资源并且重新抛出异常。

虽然在抛出异常时,会调用析构函数,所有资源都会被清理,但是在构造函数中抛出异常缺不会调用析构函数,会造成内存泄露。
技巧:
在构造函数中捕获异常,用于释放资源
在对象的构造函数中分配资源,并且在对象的析构函数中释放资源
可以讲资源分配称为局部对象生命周期的一部分,如果某次分配失败了,在栈反解的时候,其他已经获得所需资源的对象能够恰当的被清理。资源获得式初始化RAII,他可以使对象对资源控制的事件与对象的生命周期相等。

不捕获异常

如果try之后的异常处理器不能捕获所抛出的异常,那么就会被传递给位于更高层次的语境中的异常处理器,这个过程会持续进行
但是如果没有一个异常处理器能够捕获这种异常,特殊的库函数terminate()会被自动调用。默认情况下,他会调用abort使程序执行异常终止而退出。

set_terminate函数,可以设置读者自己的terminate函数,返回被替换的指向terminate函数的指针。如果terminate函数被调用,就意味着问题已经无法解决了。

可选的异常规格说明

告诉函数使用者可能会抛出的异常
void func() throw (int , char);
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: