为什么Delphi在动态对象创建过程中调用了错误的构造函数?
在动态对象创建过程中调用的virtual Create()方法不正确,我遇到了问题。调用父方法而不是Decentant方法 我已经看了这些帖子,但想不出来:为什么Delphi在动态对象创建过程中调用了错误的构造函数?,delphi,dynamic,Delphi,Dynamic,在动态对象创建过程中调用的virtual Create()方法不正确,我遇到了问题。调用父方法而不是Decentant方法 我已经看了这些帖子,但想不出来: 这里呢 我有以下课程: TCellObj = class(TPhysicsObj) ... public constructor Create(RgnMgr : TObject); virtual; //RgnMgr should be TRgnManager destructor Destroy; .
这里呢 我有以下课程:
TCellObj = class(TPhysicsObj)
...
public
constructor Create(RgnMgr : TObject); virtual; //RgnMgr should be TRgnManager
destructor Destroy;
...
end;
TCellObjClass = Class of TCellObj;
--------------------------------
TCellTrialAObj = class(TCellObj)
...
public
...
constructor Create(RgnMgr : TObject); virtual; //RgnMgr should be TRgnManager
end;
--------------------------------
TRgnManager = class (TObject)
...
public
function NewCell(ClassRef : TCellObjClass) : TCellObj;
...
end;
....
function TRgnManager.NewCell(ClassRef : TCellObjClass) : TCellObj;
var CellObj : TCellObj;
begin
CellObj := ClassRef.Create(Self);
CellObj.DefaultInitialize;
CellObj.Color := TAlphaColors.Slategray;
FCellsList.Add(CellObj); //This will own objects.
SetSelection(CellObj);
Result := CellObj;
end;
最后,我通过以下行开始创建动态对象的过程:
RgnManager.NewCell(TCellTrialAObj);
我的目标是让TRgnManager.NewCell根据作为参数传入的派生类创建TCellObj的任何Decentant。我将在使用期间将结果类型转换为适当的类类型
当我使用NewCell中的调试器逐步完成代码时,Evaluate/Modify工具告诉我ClassRef=Tcelltriaolaobj与预期的一样
但是当我进入ClassRef.Create(self)行时,它会转到TCellObj.Create(),而不是我所期望的TCellTrialAObj.Create()。这是我不明白的部分
将结果分配给CellObj后,Evaluate/Modify工具告诉我CellObj.ClassName='Tcelltriaolaobj'
ClassRef的函数是TCellTrialAObj的,那么为什么Create()函数不调用TCellTrialAObj.Create()
提前谢谢
另外,我使用的是Embarcadero®Delphi 10西雅图版本23.0.22248.5795
附录
我使用上面链接中的示例拼凑了下面的函数。它似乎可以工作,并调用TCellTrialAObj.Create。但我不明白到底是怎么做的,为什么,或者我是否真的做对了。有人能解释一下吗
function TRgnManager.NewCell(ClassRef : TCellObjClass) : TCellObj;
var CellObj : TCellObj;
RT : TRttiType;
C : TRttiContext;
T : TRttiInstanceType;
V : TValue;
begin
C := TRttiContext.Create;
T := (C.GetType(ClassRef) as TRttiInstanceType);
V := T.GetMethod('Create').Invoke(T.metaClassType,[self]);
C.Free;
CellObj := V.AsObject as TCellObj;
//CellObj := ClassRef.Create(Self);
CellObj.DefaultInitialize;
CellObj.Color := TAlphaColors.Slategray;
FCellsList.Add(CellObj); //This will own objects.
SetSelection(CellObj);
Result := CellObj;
end;
编译器警告将在这里帮助您。在编译时,您可能会注意到出现警告
方法“Create”隐藏了基类型为“TCellObj”的虚拟方法
。这是因为当我们推断您希望子代TCellTrialAObj
的构造函数为virtual
时,您已将其声明为override
这里有一个简单的示例演示您想要的功能
program Project1;
{$APPTYPE CONSOLE}
type
TCellObj = class
public
constructor Create; virtual;
end;
TCellObjClass = Class of TCellObj;
TCellTrialAObj = class(TCellObj)
public
constructor Create; override;
end;
constructor TCellObj.Create;
begin
WriteLn('TCellObj');
end;
constructor TCellTrialAObj.Create;
begin
WriteLn('Calling base constructor...');
inherited;
WriteLn('...and now in TCellTrialAObj constructor');
end;
function NewCell(ClassRef : TCellObjClass) : TCellObj;
var
CellObj : TCellObj;
begin
CellObj := ClassRef.Create;;
Result := CellObj;
end;
var
LCellObj : TCellObj;
begin
LCellObj := NewCell(TCellTrialAObj);
ReadLn;
end.
另一方面,这里您使用注释来建议类型限制:
constructor Create(RgnMgr : TObject); virtual; //RgnMgr should be TRgnManager
但是,可以对TRgnMgr
类进行前向声明,并在以后对其进行完全定义,从而允许您包含更加健壮的形式类型限制
TRgnMgr = class; { Declare type... }
TCellObj = class
public
constructor Create(RgnMgr : TRgnMgr); virtual;
end;
TCellObjClass = Class of TCellObj;
TCellTrialAObj = class(TCellObj)
public
constructor Create(RgnMgr : TRgnMgr); override;
end;
TRgnMgr = class { but define it later }
private
FFoo : integer;
end;
@kdtop确实看到了我关于类型限制的编辑-我想这也会很有意思。@kdtop除了理解多态性之外,我希望你能从中吸取教训:“永远不要忽略编译器警告”。他们告诉你,如果不是完全错误的话,至少有些事情是可疑的。您可以随时修改代码,使其不会发出警告。因此,您应该注意理解并消除您看到的任何编译器警告。@kdtop您是对的,但我可能会认为,如果这些类直接相互依赖,那么它们可能属于同一个单元。@kdtop根据您向Craig提出的问题,我想这可能是一个新的单独问题的主题。参见:@kdtop正如J所说,通常应该问一个新问题。但在这种情况下,如果你搜索,你会发现这个和相关的问题已经在这里提出过。在google中尝试以下操作:
site:stackoverflow.com delphi“隐藏基本的虚拟方法”
。这是Delphi的一个所谓的“特性”,它抑制了关于破坏层次结构中多态性的警告。注意:它不能解决任何问题,你最好重新考虑你的OO设计。您不需要隐藏任何虚拟方法。您是否沉浸在警告和提示中,或者为什么错过编译器告诉您出了什么问题?析构函数也应该对其进行重写。您可以在TRgnManager上使用forward声明,以便将其用作param。代码中的问题比您识别的单个问题多得多。@StefanGlienke和DavidHeffernan感谢您的反馈。当我试图教育自己并避免将来出现类似问题时,请参阅其他评论。