为什么Delphi在动态对象创建过程中调用了错误的构造函数?

为什么Delphi在动态对象创建过程中调用了错误的构造函数?,delphi,dynamic,Delphi,Dynamic,在动态对象创建过程中调用的virtual Create()方法不正确,我遇到了问题。调用父方法而不是Decentant方法 我已经看了这些帖子,但想不出来: 这里呢 我有以下课程: TCellObj = class(TPhysicsObj) ... public constructor Create(RgnMgr : TObject); virtual; //RgnMgr should be TRgnManager destructor Destroy; .

在动态对象创建过程中调用的virtual Create()方法不正确,我遇到了问题。调用父方法而不是Decentant方法

我已经看了这些帖子,但想不出来:

这里呢

我有以下课程:

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感谢您的反馈。当我试图教育自己并避免将来出现类似问题时,请参阅其他评论。