能否这样写构造函数?(50分)

  • 主题发起人 主题发起人 twt2000
  • 开始时间 开始时间
T

twt2000

Unregistered / Unconfirmed
GUEST, unregistred user!
在构造函数中
if 条件 then
inherited create
else
free;

 
No way!
如果条件为假,除非你在Create之后什么也不干,否则,只要一访问类的成员,就会报非法访问错误。
(连Free都不行)。

下面是我的实验代码:

type
TMy=class
Str:String;
constructor Create;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
My:TMy;
begin
My:=TMy.Create;
My.Free
//1. 出现非法操作
if My=nil then //如果将1删掉,在此处会发现My<>nil
exit;
My.Str:='asdf'
//2. 出现非法操作
My.Free;
end;

{ TMy }

var
mm:Integer=1;
constructor TMy.Create;
begin
if mm=2 then
inherited
else
Free;
end;
 
在 constructor 中调用 Free 是不行的,因为此时对象实例还未完全被构造,但可以在
constructor 中引发异常,则对象不会被构造起来。如
constructor TMyClass.Create;
begin
if False then
inherited
else
Abort
//引发异常
end;
对于这样的 constructor, 调用前必须先将对象变量设为 nil,调用后再检测对象变量是否
为 nil,不为 nil 表明对象实例已成功构造,才可以继续访问对象的字段或方法。如:
procedure TForm1.Button1Click(Sender: TObject);
var
My:TMyClass;
begin
My := nil;//必须先初始化为 nil
My:=TMyClass.Create;
if Assign(My) then //My 不为 nil 表明构造成功
begin
My.DoSomthing;
My.Free;
end;
end;
 
引发异常的方法是对的。delphi的源代码里面就有类似的例子。
比如如果某一个控件必需放置在某一种容器中,如果设计窗体的时候尝试
把它放置在别的地方,就会引发一个异常,说什么“必须放置在XXX上”之类的,
同时该控件没有创建(没有构造出来)。
 
to 一个过客,
请教一个困扰我很长时间的问题:在 constructor 中引发一个异常是否合适?
按照对象的实现方法,首先应当创建类所有变量(static 除外)的副本,并且 this 指针
指向这个地址区域。然后执行 constructor,如果这个时候引发异常,this 指针指向的
内存区域当然没有销毁,更糟糕的是你不敢调用 destructor,因为它很可能假定创建成功
并且需要使用当中的数据,这就会出错。
这个问题究竟应当如何解决?(很多国外的书都采取避之不谈的态度,有些更是直接指出
不要在 constructor 中引发异常)
 
在Delphi里,建立类实例的代码会被一个异常处理段包括,因此在create时
完全可以引发异常,Delphi会自动处理异常并释放实例,源代码如下

function _ClassCreate(AClass: TClass
Alloc: Boolean): TObject;
asm
{ -> EAX = pointer to VMT }
{ <- EAX = pointer to instance }
PUSH EDX
PUSH ECX
PUSH EBX
TEST DL,DL
JL @@noAlloc
CALL dword ptr [EAX].vmtNewInstance // 建立一个新实例
@@noAlloc:
{$IFNDEF PC_MAPPED_EXCEPTIONS}
XOR EDX,EDX
LEA ECX,[ESP+16]
MOV EBX,FS:[EDX]
MOV [ECX].TExcFrame.next,EBX
MOV [ECX].TExcFrame.hEBP,EBP
MOV [ECX].TExcFrame.desc,offset @desc // 异常处理入口地址
MOV [ECX].TexcFrame.ConstructedObject,EAX { trick: remember copy to instance }
MOV FS:[EDX],ECX
{$ENDIF}
POP EBX
POP ECX
POP EDX
RET

{$IFNDEF PC_MAPPED_EXCEPTIONS}
@desc: // 异常处理开始
JMP _HandleAnyException // 类似直接用try ... except ... end,处理所有异常情况

{ destroy the object }

MOV EAX,[ESP+8+9*4]
MOV EAX,[EAX].TExcFrame.ConstructedObject // 是否已经建立实例
TEST EAX,EAX // 没有建立则直接退出处理代码,重新引发异常
JE @@skip
MOV ECX,[EAX]
MOV DL,$81
PUSH EAX
CALL dword ptr [ECX].vmtDestroy // 调用类的Destroy方法
POP EAX
CALL _ClassDestroy
@@skip:
{ reraise the exception }
CALL _RaiseAgain
{$ENDIF}
end;

因此在Create里面完全可以引发异常
 
多人接受答案了。
 
后退
顶部