设为首页 加入收藏

TOP

Delphi高手突破(三) Delphi高级进阶(四)
2017-10-10 12:05:08 】 浏览:2042
Tags:Delphi 高手 突破 高级 进阶
踪试一下,无论在发生异常(即调用了 SimulateError)的情况下,还是正常退出(不
调用 SimulateError 或将 SimulateError 的调用改为 Exit)的情况下,AStream.Free()都会得
到执行。
同时拥有 try…except 和 try…finally,应该说是 Delphi 程序员的一种幸运,值得庆幸。
只是,我们想得到的会更多,会希望拥有
 
try
  ……
except
  ……
finally
 
这样的结构,只是目前还得不到满足。虽然可以用
 
try
 try
   ……
 except
   ……
 end
finally
  ……
end;
 
来取代,但显然不如所希望的那样结构美观和优雅。这不能不说是一种遗憾,让我们寄希
望于下一个 Delphi 版本吧!
 
·58·

异常及错误处理
3.4  构造函数与异常
这个话题在 C++社区中经常会被提起,而在 Delphi 社区中似乎从来没有人注意过,也
许由于语言的特性而使得 Delphi 程序员不必关心这个问题。但我想,Delphi 程序员也应该
3
对该问题有所了解,知道语言为我们提供了什么而使得我们如此轻松,不必理会它。正所
谓“身在福中须知福”。
我们知道,类的构造函数是没有返回值的,因此如果构造函数构造对象失败,则不可
能依靠返回错误代码来解决。那么,在程序中如何标识构造函数的失败呢?最“标准”的
方法就是:抛出一个异常。
构造函数失败,意味着对象的构造失败。那么抛出异常之后,这个“半死不活”的对
象会被如何处理呢?
在此,读者有必要先对 C++对这种情况的处理方式有一个了解。
在 C++中,构造函数抛出异常后,析构函数不会被调用。这种做法是合理的,因为此
时对象并没有被完整构造。
如果构造函数已经做了一些诸如分配内存、打开文件等操作,那么 C++类需要有自己
的成员来记住做过哪些动作。当然,这样做对于类的实现者来说非常麻烦。因此,一般 C++
类的实现者都避免在构造函数中抛出异常(可以提供一个诸如 Init 和 UnInit 的成员函数,
由构造函数或类的客户去调用它们,以处理初始化失败的情况)。而每一本 C++的经典著
作所提供的方案都是使用智能指针(STL 的标准类 auto_ptr)。
在 Object Pascal 中,这个问题变得非常简单,程序员不必为此大费周折。如果 Object
Pascal 的类在构造函数中抛出异常,则编译器会自动调用类的析构函数(由于析构函数不
允许被重载,可以保证只有惟一一个析构函数,因此编译器不会迷惑于多个析构函数之中)。
析构函数中一般会析构成员对象,而 Free()方法保证了不会对 nil 对象(即尚未被创建的成
员对象)调用析构函数,因此在使得代码简洁优美的前提下,又保证了安全。
以下的程序演示了构造函数中抛出异常后,Object Pascal 编译器所作的处理方法。
首先定义 TMyClass: 
 
type 
  TMyClass = class
 private
    FStr : PChar; // 字符串指针
 public
   constructor Create();
    destructor Destroy(); override;
 end;
 
然后实现 TMyClass,并让它的构造函数中抛出异常:
 
·59·

Delphi  高手突破     
constructor TMyClass.Create();
begin
  FStr := StrAlloc(10); // 构造函数中为字符串指针分配内存
 StrCopy(FStr, 'ABCDEFGHI');
  raise Exception.Create('error'); // 抛出异常,没有理由
end;
 
destructor TMyClass.Destroy();
begin
 StrDispose(FStr); // 析构函数中释放内存
 WriteLn('Free Resource');
end;
 
最后,编写程序主流程的代码。主流程中首先创建 TMyClass 类的实例:
 
var
  Obj : TMyClass;
  i : integer;
begin
 try
    Obj := TMyClass.Create();
    // Obj.Free(); // 不调用析构函数,但发生异常时,编译器自动调用了析构函数
   WriteLn('Succeeded');
 except
    Obj := nil;
   WriteLn('Failed');
 end;
 
 Read(i); // 暂停屏幕,以便观察运行结果
end.
 
这段代码中,创建 TMyClass 类的实例时遇到了麻烦,因为 TMyClass 的构造函数抛出
了异常,但这段代码执行结果却是:
 
Free Resource
Failed
 
出现了“Free Resource”,说明发生异常后,析构函数被调用了。而这正是在构造函
数抛出异常之后,编译器自动调用析构函数的结果。
因此,如果类的说明文档或类的作者告知你,类的构造函数可能会抛出异常,那就要
记得用 try…except 包住它!
 
·60·

异常及错误处理
C++与 Object Pascal 对于构造函数抛出异常后的不同处理方式,其实正是两种语言的
设计思想的体现。C++秉承 C 语言的风格,注重效率,一切交给程序员来掌握,编译器不
做多余动作;Object Pascal 继承 Pascal 的风格,注重程序的美学意义,编译器帮助程序员
完成复杂的工作。
3.5  小    结  3
异常是面向对象编程带来的非常好的工具,不加以利用是很可惜的。但是,正如万事
都有个“度”,滥用异常也是不可取的。使用异常不是没有代价,它会增加程序的负担,
编写若干 try...except 和编写数以千计的 try...except 之间是有很大区别的。
同时,也不必过分害怕由它所带来的负担。其实,既然已经使用了 Delphi,其实就已
经在使用异常了,也许只是自己还不知道。听听 Chalie Calverts 的忠告:“在似乎有用的
时候,就应该使用 try...except 块。但是要试着让自己对这种技术的热情不要太过分”。

 

首页 上一页 1 2 3 4 下一页 尾页 4/4/4
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇[修正] Firemonkey TSelection 控.. 下一篇[修正] 移动平台曲线不平滑的问题..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目