分享
 
 
 

异常处理(四)

王朝other·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

异常处理(四) Danny Kalev 翻译: cppbug cpp_bug@hotmail.com

在对象构造和销毁时出现异常

构造函数和析构函数被自动调用,并且它们不能够利用返回值来表明发生运行期错误。从表面上看,在对象构造和销毁时抛出一个异常似乎是报告运行期错误的最好方法。但事实上你还必须考虑一些额外的因素。你尤其应该对从析构函数中抛出异常保持警惕。

从析构函数中抛出异常是危险的

从析构函数中抛出异常是不应该被推荐的,这是因为一个析构函数可能会在另一个异常进行stack unwinding的时候被调用,在这种情况下,异常处理机制就会调用terminate()终止程序。如果你真的想从一个析构函数中抛出异常的话,一种可取的做法是首先检查一下是否还有未被捕获的异常存在。

检查未被捕获的异常

一个异常被捕获是在它相应的handler被找到的情况下。为了检查一个异常是否被捕获,你可以使用标准函数uncaught_exception()(它被定义在标准头文件<stdexcept>)。例如:

class FileException{};

File::~File() throw (FileException)

{

if ( close(file_handle) != success) // failed to close current file?

{

if (uncaught_exception() == true ) // is there any uncaught exception

//being processed currently?

return; // if so, do not throw an exception

throw FileException(); // otherwise, it is safe to throw an exception

// to signal an error

}

return; // success

}

然而,一个更好的选择是直接在析构函数内部处理异常,而不是让他们扩散到外面。例如:

void cleanup() throw (int);

class C

{

public:

~C();

};

C::~C()

{

try

{

cleanup();

}

catch(int)

{

//handle the exception within the destructor

}

}

如果一个异常被函数cleanup()抛出,那么它在析构函数内部就被处理。否则,被抛出的异常就会传播到析构函数的外部,并且如果这个析构函数是在stack unwinding 的过程中被调用,那么程序将会通过terminate()的调用而终止。

全局对象:构造和销毁

我们都知道,全局对象的构造发生在程序开始之前。因此,任何从全局对象的构造函数中抛出的异常将不会被捕获。这一点对于全局对象的析构函数也是一样的-----全局对象的析构函数在程序结束之后被运行。因此,一个从全局对象的析构函数中抛出的异常也不会被捕获。

高级异常处理技术

简单的try-throw-catch模型可以被扩展来处理更为复杂的运行期错误。这一节将会讨论一些更为高级的异常处理技术,包括异常层次,重新抛出异常,function try blocks以及auto_ptr 类。

标准异常

C++定义了一个标准异常层次,当在运行时发生反常情形时抛出。标准异常类从std::exception(在<stdexcept>头文件中定义)派生。这一层次使得应用程序能够在单一的catch语句中捕获这些异常:

catch (std::exception& exc)

{

// handle exception of type std::exception as well as

//any exception derived from it

}

那些通过语言内建操作符抛出的标准异常是:

std::bad_alloc //by operator new

std::bad_cast //by operator dynamic_cast < >

std::bad_typeid //by operator typeid

std::bad_exception //thrown when an exception specification of

所有的标准异常都提供了成员函数what(),它返回一个用来描述异常细节的字符串。注意,标准库还有另外一个被它的组件抛出的的异常集合。

异常处理层次

异常在一个自下向上的层次中捕获:派生层次越深的异常越先被处理,例如:

#include <stdexcept>

#include <iostream>

using namespace std;

int main()

{

try

{

char * buff = new char[100000000];

//...use buff

}

catch(bad_alloc& alloc_failure) // bad_alloc is

//derived from exception

{

cout<<"memory allocation failure";

//... handle exception thrown by operator new

}

catch(exception& std_ex)

{

cout<< std_ex.what() <<endl;

}

catch(...) // exceptions that are not handled elsewhere are caught here

{

cout<<"unrecognized exception"<<endl;

}

return 0;

}

派生层次越深的handler必须出现在其基类的前面。这是因为handler的匹配过程是按照出现的顺序进行的。因此有可能某个handler永远不会被执行,例如,把一个处理派生类异常的handler放在处理基类异常的handler的后面。例如:

catch(std::exception& std_ex) //bad_alloc exception is always handled here

{

//...handle the exception

}

catch(std::bad_alloc& alloc_failure) //unreachable

{

cout<<"memory allocation failure";

}

重新抛出异常

异常的抛出表明了一种反常的状态。先捕获到异常的handler试图解决这个问题,但是它如果没有成功或者只完成了部分恢复,那么它可以重新抛出这个异常,让更高一层的try block来处理它。基于这种目的,try blocks可以在一个分等级的顺序上进行嵌套,使得一个从低层重新抛出的异常能够被重新捕获。重新抛出用一个没有操作数的throw语句来表示。例如:

#include <iostream>

#include <string>

using namespace std;

enum {SUCCESS, FAILURE};

class File

{

public: File (const char *) {}

public: bool IsValid() const {return false; }

public: int OpenNew() const {return FAILURE; }

};

class Exception {/*..*/}; //general base class for exceptions

class FileException: public Exception

{

public: FileException(const char *p) : s(p) {}

public: const char * Error() const { return s.c_str(); }

private: string s;

};

void func(File& );

int main()

{

try //outer try

{

File f ("db.dat");

func; // 1

}

catch(...) // 7

//this handler will catch the re-thrown exception;

//note: the same exception type is required

{

cout<<"re-thrown exception caught";

}

return 0;

}

void func(File & f)

{

try //inner try

{

if (f.IsValid() == false )

throw FileException("db.dat"); // 2

}

catch(FileException &fe) // 3

//first chance to cope with the exception

{

cout<<"invalid file specification" <<fe.Error()<<endl;

if (f.OpenNew() != SUCCESS) (5)

//re-throw the original exception and let a higher handler deal with it

throw; // 6

}

}

在上面的例子中,函数func()在main()中的try block里被调用(1)。第二个在func()中的try block抛出一个FileException类型的异常(2)。这个异常被func()内的catch block所捕获(3)。那个catch block试图通过打开一个新文件进行补救,但是失败了(5),并且FileException异常被重新抛出(6)。最终,那个重新抛出的异常被main()中的catch(…)所捕获(7)。

Function try Blocks

Function try blocks是一个函数体本身就含有一个try block以及它的相关handler的函数。比如:

class Bad{};

void foo()try

{

throw Bad();

}

catch(...)

{

std::cout<<"error catch!!";

}

function try block使得一个handler能够捕获构造函数中以及初始化列表中发生的异常。然而,它并不像普通异常的handler,function try block很少能够捕获异常继续对象的构建。这是因为被部分构造的对象要被销毁。另外,一个function try block的handler不能执行返回语句(或者说,handler必须通过一个throw离开)。那么究竟function try block的用处是什么呢?handler使得你可以抛出另一个异常而不是你刚才捕获的那个,这样可以阻止一个违背exception specification的情况发生。例如:

class X{};

C::C(const std::string& s) throw (X) // allowed to throw X only

try

: str(s) // str's constructor might throw a bad_alloc exception,

// might violate C's exception specification

{

// constructor function body

}

catch (...) //handle any exception thrown from ctor initializer or ctor body

{

//...

throw X(); //replace bad_alloc exception with an exception of type X

}

在这个例子中,一个string对象首先被创建作为class c 的一个成员。String在它的创建过程中可能抛出一个bad_alloc异常。那个function try block能够捕获bad_alloc异常并且抛出类型为x的异常使得它满足c的构造函数的exception specification的需要。

(未完待续)

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有