分享
 
 
 

C与C++中的异常处理10

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

1. 从私有子对象中产生的异常

几部分来,我一直展示了一些技巧来捕获从对象的构造函数中抛出的异常。这些技巧是在异常从构造函数中漏出来后处理它们。有时,调用者需要知道这些异常,但通常(如我所采用的例程中)异常是从调用者并不关心的私有子对象中爆发的。使得用户要关心“不可见”的对象表明了设计的脆弱。

在历史上,(可能抛异常)的构造函数的实现者没有简单而健壮的解决方法。看这个简单的例子:

#include <stdlib.h>

class buffer

{

public:

explicit buffer(size_t);

~buffer();

private:

char *p;

};

buffer::buffer(size_t const count)

: p(new char[count])

{

}

buffer::~buffer()

{

delete[] p;

}

static void do_something_with(buffer &)

{

}

int main()

{

buffer b(100);

do_something_with(b);

return 0;

}

buffer的构造函数接受字符数目并从自由空间分配内存,然后初始化buffer::p指向它。如果分配失败,构造函数中的new语句产生一个异常,而buffer的用户(这里是main函数)必须捕获它。

1.1 try块

不幸的是,捕获这个异常不是件容易事。因为抛出来自buffer::buffer,所有buffer的构造函数的调用应该被包在try块中。没脑子的解决方法:

try

{

buffer b(count);

}

catch (...)

{

abort();

}

do_something_with(b); // ERROR. At this point,

// 'b' no longer exists

是不行的。do_something_with()的调用必须在try块中:

try

{

buffer b(100);

do_something_with(b);

}

catch (...)

{

abort();

}

//do_something_with(b);

(免得被说闲话:我知道调用abort()来处理这个异常有些过份。我只是用它做个示例,因为现在关心的是捕获异常而不是处理它。)

虽然有些笨拙,但这个方法是有效的。接着考虑这样的变化:

static buffer b(100);

int main()

{

// buffer b(100);

do_something_with(b);

return 0;

}

现在,b被定义为全局对象。试图将它包入try块

try // um, no, I don't think so

{

static buffer b;

}

catch (...)

{

abort();

}

int main()

{

do_something_with(b);

return 0;

}

将不能被编译。

1.2 暴露实现

每个例子都显示了buffer设计上的基本缺陷:buffer的接口以外的实现细节被暴露了。在这里,暴露的细节是buffer的构造函数中的new语句可能失败。这个语句用于初始化私有子对象buffer::p――一个main函数和其它用户不能操作甚至根本不知道的子对象。当然,这些用户更不应该被要求必须关注这样的子对象抛出的异常。

为了改善buffer的设计,我们必须在构造函数中捕获异常:

#include <stdlib.h>

class buffer

{

public:

explicit buffer(size_t);

~buffer();

private:

char *p;

};

buffer::buffer(size_t const count)

: p(NULL)

{

try

{

p = new char[count];

}

catch (...)

{

abort();

}

}

buffer::~buffer()

{

delete[] p;

}

static void do_something_with(buffer &)

{

}

int main()

{

buffer b(100);

do_something_with(b);

return 0;

}

异常被包含在构造函数中。用户,比如main()函数,从不知道异常存在过,世界又一次清静了。

1.3 常量成员

也这么做?注意,buffer::p一旦被设置过就不能再被改动。为避免指针被无意改动,谨慎的设计是将它申明为const:

class buffer

{

public:

explicit buffer(size_t);

~buffer();

private:

char * const p;

};

很好,但到了这步时:

buffer::buffer(size_t const count)

{

try

{

p = new char[count]; // ERROR

}

catch (...)

{

abort();

}

}

一旦被初始化,常量成员不能再被改变,即使是在包含它们的对象的构造函数体中。常量成员只能被构造函数的成员初始化列表设置一次。

buffer::buffer(size_t const count)

: p(new char[count]) // OK

这让我们回到了段落一中,又重新产生了我们最初想解决的问题。

OK,这么样如何:不用new语句初始化p,换成用内部使用new的辅助函数来初始化它:

char *new_chars(size_t const count)

{

try

{

return new char[count];

}

catch (...)

{

abort();

}

}

buffer::buffer(int const count)

: p(new_chars(count))

{

// try

// {

// p = new char[count]; // ERROR

// }

// catch (...)

// {

// abort();

// }

}

这个能工作,但代价是一个额外函数却仅仅用来保护一个几乎从不发生的事件。

1.4 函数try块

(WQ注:后面会讲到,function try块不能阻止构造函数的抛异常动作,它其实只起异常过滤的功能!!!见P14.3)

我在上面这些建议中没有发现哪个能确实令人满意。我所期望的是一个语言级的解决方案来处理部分构造子对象问题,而又不引起上面说到的问题。幸运的是,语言中恰好包含了这样一个解决方法。

在深思熟虑后,C++标准委员会增加了一个叫做“function try blocks”的东西到语言规范中。作为try块的堂兄弟,函数try块捕获整个函数定义中的异常,包括成员初始化列表。不用奇怪,因为语言最初没有被设计了支持函数try块,所以语法有些怪:

buffer::buffer(size_t const count)

try

: p(new char[count])

{

}

catch

{

abort();

}

看起来想是通常的try块后面的{}实际上是划分构造函数的函数体的。在效果上,{}有双重作用,不然,我们将面对更别扭的东西:

buffer::buffer(int const count)

try

: p(new char[count])

{

{

}

}

catch

{

abort();

}

(注意:虽然嵌套的{}是多余的,这个版本能够编译。实际上,你可以嵌套任意重{},直到遇到编译器的极限。)

如果在初始化列表中有多个初始化,我们必须将它们放入同一个函数try块中:

buffer::buffer()

try

: p(...), q(...), r(...)

{

// constructor body

}

catch (std::bad_alloc)

{

// ...

}

和普通的try块一样,可以有任意个异常处理函数:

buffer::buffer()

try

: p(...), q(...), r(...)

{

// constructor body

}

catch (std::bad_alloc)

{

// ...

}

catch (int)

{

// ...

}

catch (...)

{

// ...

}

古怪的语法之外,函数try块解决了我们最初的问题:所有从buffer子对象的构造函数抛出的异常留在了buffer的构造函数中。

因为我们现在期望buffer的构造函数不抛出任何异常,我们应该给它一个异常规格申明:

explicit buffer(size_t) throw();

接着一想,我们应该是个更好点的程序员,于是给我们所有函数加了异常规格申明:

class buffer

{

public:

explicit buffer(size_t) throw();

~buffer() throw();

// ...

};

// ...

static void do_something_with(buffer &) throw()

// ...

Rounding Third and Heading for Home

对我们的例子,最终版本是:

#include <stdlib.h>

class buffer

{

public:

explicit buffer(size_t) throw();

~buffer() throw();

private:

char *const p;

};

buffer::buffer(size_t const count)

try

: p(new char[count])

{

}

catch (...)

{

abort();

}

buffer::~buffer()

{

delete[] p;

}

static void do_something_with(buffer &) throw()

{

}

int main()

{

buffer b(100);

do_something_with(b);

return 0;

}

用Visual C++编译,自鸣得意地坐下来,看着IDE的提示输出。

syntax error : missing ';' before 'try'

syntax error : missing ';' before 'try'

'count' : undeclared identifier

'<Unknown>' : function-style initializer appears

to be a function definition

syntax error : missing ';' before 'catch'

syntax error : missing ';' before '{'

missing function header (old-style formal list?)

噢!

Visual C++还不支持函数try块。在我测试过的编译器中,只有Edison Design Group C++ Front End version 2.42认为这些代码合法。

(顺便提一下,我特别关心为什么编译将第一个错误重复了一下。可能它的计算你第一次会不相信。)

如果你坚持使用Visual C++,你可以使用在介绍函数try块前所说的解决方法。我喜欢使用额外的new封装函数。如果你认同,考虑将它做成模板:

template <typename T>

T *new_array(size_t const count)

{

try

{

return new T[count];

}

catch (...)

{

abort();

}

}

// ...

buffer::buffer(size_t const count)

: p(new_array<char>(count))

{

}

这个模板比原来的new_chars函数通用得多,对char以外的类型也有能工作。同时,它有一个隐蔽的异常相关问题,而我将在下次谈到。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
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- 王朝網路 版權所有