再谈异常——谈C++与Object Pascal中的构造函数与异常
来源:互联网 发布:开源财务记账软件 php 编辑:程序博客网 时间:2024/05/08 08:38
再谈异常——谈C++与Object Pascal中的构造函数与异常
作者:Nicrosoft(nicrosoft@sunistudio.com) 2001.9.15
个人主页:http://www.sunistudio.com/nicrosoft/
东日文档:http://www.sunistudio.com/asp/sunidoc.asp
我们知道,类的构造函数是没有返回值的,如果构造函数构造对象失败,不可能依靠返回错误代码。那么,在程序中如何标识构造函数的失败呢?最“标准”的方法就是:抛出一个异常。
构造函数失败,意味着对象的构造失败,那么抛出异常之后,这个“半死不活”的对象会被如何处理呢?这就是本文的主题。
在C++中,构造函数抛出异常后,析构函数不会被调用。这是合理的,因为此时对象并没有被完整构造。也就是说,如果构造函数已经做了一些诸如分配内存、打开文件等操作的话,那么类需要有自己的成员来记住做过哪些动作。在C++中,经典的解决方案是使用STL的标准类auto_ptr,这在每一本经典C++著作中都有介绍,我在这里就不多说了。在这里,我想再介绍一种“非常规”的方式,其思想就是避免在构造函数中抛出异常。我们可以在类中增加一个 Init(); 以及 UnInit();成员函数用于进行容易产生错误的资源分配工作,而真正的构造函数中先将所有成员置为NULL,然后调用 Init(); 并判断其返回值(或者捕捉 Init()抛出的异常),如果Init();失败了,则在构造函数中调用 UnInit(); 并设置一个标志位表明构造失败。UnInit()中按照成员是否为NULL进行资源的释放工作。示例代码如下:
class A
{
private:
char* str;
int failed;
public:
A();
~A();
int Init();
int UnInit();
int Failed();
};
A::A()
{
str = NULL;
try
{
Init();
failed = 0;
}
catch(...)
{
failed = 1;
UnInit();
}
}
A::~A()
{
UnInit();
}
int A::Init()
{
str = new char[10];
strcpy(str, "ABCDEFGHI");
throw 10;
return 1;
}
int A::UnInit()
{
if (!str)
{
delete []str;
str = NULL;
}
printf("Free Resource");
return 1;
}
int A::Failed()
{
return failed;
}
int main(int argc, char* argv[])
{
A* a = new A;
if ( a->Failed() )
printf("failed");
else
printf("succeeded");
delete a;
getchar();
return 0;
}
你会发现,在int A::Init()中包含了throw 10;的代码(产生一个异常,模拟错误的发生),执行结果是:
Free Resource
failed
Free Resource
虽然 UnInit();被调用了两次,但是由于UnInit();中做了判断(if (!str)),因此不会发生错误。而如果没有发生异常(去掉 int A::Init()中的throw 10;代码),执行结果是:
Succeeded
Free Resource
和正常的流程没有任何区别。
在Object Pascal(Delphi/VCL)中,这个问题就变得非常的简单了,因为 OP 对构造函数的异常的处理与C++不同,在Create时抛出异常后,编译器会自动调用析构函数Destroy,并且会判断哪些资源被分配了,实行自动回收。因此,其代码也变得非常简洁,如下:
type
A = class
private
str : PChar;
public
constructor Create();
destructor Destroy(); override;
end;
constructor A.Create();
begin
str := StrAlloc(10);
StrCopy(str, 'ABCDEFGHI');
raise Exception.Create('error');
end;
destructor A.Destroy();
begin
StrDispose(str);
WriteLn('Free Resource');
end;
var oa : A;
i : integer;
begin
try
oa := A.Create();
WriteLn('Succeeded');
oa.Free();
except
oa := nil;
WriteLn('Failed');
end;
Read(i);
end.
在这段代码中,如果构造函数抛出异常(即Create中含有raise Exception.Create('error');),执行的结果是:
Free Resource
Failed
此时的“Free Resource”输出是由编译器自动调用析构函数所产生的。而如果构造函数正常返回(即不抛出异常),则执行结果是:
Succeeded
Free Resource
此时的“Free Resource”输出是由 oa.Free()的调用产生的。
综上,C++与Object Pascal对于构造函数抛出异常后的不同处理方式,其实正是两种语言的设计思想的体现。C++秉承C的风格,注重效率,一切交给程序员来掌握,编译器不作多余动作。Object Pascal继承Pascal的风格,注重程序的美学意义(不可否认,Pascal代码是全世界最优美的代码),编译器帮助程序员完成复杂的工作。两种语言都有存在的理由,都有存在的必要!而掌握它们之间的差别,能让你更好地控制它们,达到自由的理想王国。
<script type="text/javascript"><!--google_ad_client = "pub-2947489232296736";/* 728x15, 创建于 08-4-23MSDN */google_ad_slot = "3624277373";google_ad_width = 728;google_ad_height = 15;//--></script><script type="text/javascript"src="http://pagead2.googlesyndication.com/pagead/show_ads.js"></script>- 再谈异常——谈C++与Object Pascal中的构造函数与异常
- 再谈异常——谈C++与Object Pascal中的构造函数与异常
- C++-面试题:深度拷贝与构造函数中的异常
- java中的Object与异常
- delphi 构造函数与异常
- C++构造函数与析构函数中的“异常”
- 关于Object-c 中的构造函数与析构函数
- 异常与构造函数、析构函数
- c语言中的异常处理,setjmp 与 longjmp函数
- Object-C基础(10)——异常处理、反射、动态调用方法与内存回收
- 构造函数中的异常处理
- 【effective c++】C++构造函数、析构函数与抛出异常
- 异常、虚函数与构造函数、析构函数
- 构造函数与析构函数中抛出异常
- 构造函数、析构函数 与抛出异常
- C++构造函数、析构函数与抛出异常
- 构造函数与析构函数是否可以抛出异常
- C++构造函数、析构函数与抛出异常
- 通过Delphi访问Oracle数据库
- 转 一位朋友辞职引起的思考
- 滑动窗口协议
- 网页学习:实时切换CSS样式
- Sql联合查询
- 再谈异常——谈C++与Object Pascal中的构造函数与异常
- 建立自己的操作标准
- MySQL数据库函数详解(5)
- 用Delphi处理公历到农历的转换
- 网页技巧之怎样编写CSS
- Sql连接查询
- 泛型编程在非C++语言中的实现之探讨
- 怎么改善现有网站为xhtmlCSS
- MYSQL初学者使用指南