我的Delphi2006在动态创建过程中似乎调用了不正确的构造函数。
5年前我问过几乎完全相同的问题(Why does Delphi call incorrect constructor during dynamic object creation?),我已经检查过了。但是那个线程有覆盖虚拟调用的问题,我现在没有了。我也试着通过StackOverflow搜索匹配的问题,但是找不到答案。
我正在处理遗留代码,所以我没有写太多。(如果你看到下面的注解中有"//kt "添加了一些东西,那就是我)。
代码有基类TPCEItem,如下所示。注意它没有构造函数。
TPCEItem = class(TObject)
{base class for PCE items}
private
<irrelevent stuff>
public
<irrelevent stuff>
end;
接下来,是用于传递参数的类类型(更多信息见下文)。
TPCEItemClass = class of TPCEItem;
接下来我有一个子类,如下所示。注意它确实有一个构造函数。编译器不允许我在这个create方法中添加"override",因为声明它的祖先类(TObject)没有将它定义为virtual。
TPCEProc = class(TPCEItem)
{class for procedures}
protected
<irrelevent stuff>
public
<irrelevent stuff>
constructor Create;
destructor Destroy; override;
end;
然后代码有一个复制数据的函数,它是后代类型的聚合。因为这是较老的代码,这些列表中的大多数是普通的TList或TStringList,持有无类型的指针。因此,对于每个复制命令,都会传入相应的类型以供正确使用。
procedure TPCEData.CopyPCEData(Dest: TPCEData);
begin
Dest.Clear;
<irrelevent stuff>
CopyPCEItems(FVisitTypesList, Dest.FVisitTypesList, TPCEProc); //kt added
CopyPCEItems(FDiagnoses, Dest.FDiagnoses, TPCEDiag);
CopyPCEItems(FProcedures, Dest.FProcedures, TPCEProc);
CopyPCEItems(FImmunizations, Dest.FImmunizations, TPCEImm);
CopyPCEItems(FSkinTests, Dest.FSkinTests, TPCESkin);
CopyPCEItems(FPatientEds, Dest.FPatientEds, TPCEPat);
CopyPCEItems(FHealthFactors, Dest.FHealthFactors, TPCEHealth);
CopyPCEItems(FExams, Dest.FExams, TPCEExams);
<irrelevent stuff>
end;
此复制PCEItems如下所示:
procedure TPCEData.CopyPCEItems(Src: TList; Dest: TObject; ItemClass: TPCEItemClass);
var
AItem: TPCEItem;
i: Integer;
IsStrings: boolean;
Obj : TObject;
begin
if (Dest is TStrings) then begin
IsStrings := TRUE
end else if (Dest is TList) then begin
IsStrings := FALSE
end else begin
exit;
end;
for i := 0 to Src.Count - 1 do begin
Obj := TObject(Src[i]);
if(not TPCEItem(Src[i]).FDelete) then begin
AItem := ItemClass.Create; //<--- THE PROBLEMATIC LINE
if (Obj.ClassType = TPCEProc) and (ItemClass = TPCEProc) then begin //kt added if block and sub block below
TPCEProc(Obj).CopyProc(TPCEProc(AItem));
end else begin
AItem.Assign(TPCEItem(Src[i])); //kt <-- originally this line was by itself.
end;
if (IsStrings) then begin
TStrings(Dest).AddObject(AItem.ItemStr, AItem)
end else begin
TList(Dest).Add(AItem);
end;
end;
end;
end;
有问题的行如下:
AItem := ItemClass.Create;
当我使用调试器单步调试代码并停在这一行时,变量ItemClass的检查如下所示
ItemClass = TPCEProc
问题是. Create调用的是TObject. Create,而不是TPCEProc. Create,这使我没有机会示例化一些需要的TStringList,稍后会导致访问冲突错误。
有谁能帮我弄明白这是怎么回事吗?我怀疑问题出在这行上:
TPCEItemClass = class of TPCEItem;
这是因为这是一个祖先类型(即TPCEItem)的类,所以它不能正确地携带子类型(TPCEProc)的信息。但是如果这是真的,那么为什么调试器显示ItemClass = TPCEProc?
如何实现对TPCEProc.Create的调用?
我已经用Delphi编程至少30年了,我一直遇到多态性的问题,这让我很沮丧。我反复阅读这方面的文章。但我一直碰壁。
先谢了。
3条答案
按热度按时间l5tcr1uw1#
当你通过元类构造对象时,你需要把它的基类构造函数标记为虚的,如果你需要在任何子类中有一个构造函数,它们需要重写那个虚构造函数。
如果基类没有构造函数,则需要添加一个空的构造函数。
ni65a41a2#
您已经发现了问题-基类
TPCEItem
没有定义virtual
构造函数,它只是从TObject
继承了一个构造函数,而TObject
不是virtual
。因此,你不能使用
TPCEItemClass
元类类型创建任何TPCEItem
派生类的示例.为了使元类调用正确的派生类构造函数,被引用的基类必须有virtual
构造函数,例如:2nc8po8w3#
恭喜您已识别出有问题的行
但是这一行有什么问题呢?你正在从现有的类示例中调用构造函数方法。你不应该这样做。你应该只从特定的类类型中调用构造函数方法,而不是从现有的类示例中调用。
因此,为了修复代码,请将上面提到的行更改为
您可能正在考虑调用
AItem := TPCEItemClass.Create;
,因为您在上面的代码中做了next声明此声明并不意味着
TPCEItemClass
与TPCEItem
是相同的类型,而是这两种类型具有相同的类型结构,但它们实际上是两种不同的类型。顺便问一下,如果你甚至没有在你的过程中使用
CopyPCEItems
参数,而是一直使用局部变量AItem: TPCEItem
,那么ItemClass: TPCEItemClass
参数的用途是什么?至少在你显示的代码中是这样的。