Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/url/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何检测Delphi类是否具有虚拟构造函数?_Delphi_Delphi 2009_Rtti - Fatal编程技术网

如何检测Delphi类是否具有虚拟构造函数?

如何检测Delphi类是否具有虚拟构造函数?,delphi,delphi-2009,rtti,Delphi,Delphi 2009,Rtti,例如,有没有办法发现这个类(在运行时)有一个虚拟构造函数 例如,在这段代码中,我想测试Clazz引用的类是否具有虚拟构造函数: procedure Test; var Clazz: TClass; Instance: TObject; begin Clazz := TMyClass; Instance := Clazz.Create; end; 是否有一个简单的解决方案,例如使用RTTI,它可以在Delphi 6到2009中工作?查看TypInfo单元,似乎没有任何方法可以判断

例如,有没有办法发现这个类(在运行时)有一个虚拟构造函数

例如,在这段代码中,我想测试Clazz引用的类是否具有虚拟构造函数:

procedure Test;
var
  Clazz: TClass;
  Instance: TObject;
begin
  Clazz := TMyClass;
  Instance := Clazz.Create;
end;

是否有一个简单的解决方案,例如使用RTTI,它可以在Delphi 6到2009中工作?

查看TypInfo单元,似乎没有任何方法可以判断使用RTTI的方法是否是虚拟的。但是,如果您有一个类引用,您可能可以通过检查VMT来滚动您自己的方法

根据Allen Bauer的说法,在对的回答中,您可以在vmtClassName指向的值之前找到VMT的结尾。第一个用户定义的虚拟方法(如果有)位于类引用的地址。换句话说,
指针(Clazz)^
。既然您已经知道了VMT的用户定义部分的起点和终点,那么创建一个while循环来比较表中的每个指针与指向Clazz.create的方法指针的代码部分,并将其转换为TMethod应该不会太困难。如果你得到一个匹配,那么它就是一个虚拟方法。如果不是,那就不是


是的,这是一个有点黑客,但它会工作。如果有人能找到更好的解决方案,就给他们更多的力量。

你知道,我越想,我越不喜欢我给出的最终被接受的答案。问题是,编写的代码只能处理编译时已知的信息。如果Clazz被定义为一个TClass,那么将Clazz.Create放在一个TMethod中总是会给您一个指向TObject.Create的方法指针

您可以尝试将Clazz定义为“TMyClass类”。问题是,你已经有了一个虚拟构造函数,所以它会给你一个最高级别的构造函数,它可以覆盖这个构造函数。但从您的评论来看,您试图找到的似乎是一个非虚拟构造函数(使用重新引入;),它将破坏您的虚拟构造。很可能您使用的是工厂模式,这可能是一个问题

唯一的解决方案是使用RTTI查找实际附加到类的构造函数。您可以获得“名为Create的方法”的方法指针,并在我在另一个答案中解释的技巧中使用它。为此,必须声明基本虚拟构造函数已发布。这将强制所有覆盖它的方法也被发布。问题是,有人仍然可以使用重新引入在上面声明一个未发布的构造函数,那么您的方案就会崩溃。对于后代类将做什么,您没有任何保证

这个问题没有技术解决方案。唯一真正有效的是教育。您的用户需要知道这个类是由工厂实例化的(或者您需要虚拟构造函数的任何原因),如果他们在派生类中重新引入构造函数,它可能会破坏一切。在文档中写一条注释,在源代码中写一条注释。这几乎是你所能做的了。

迈克尔

我明白你的问题,但是由于你的源代码没有编译,我认为你没有抓住问题的重点;-)

我的回答是对梅森在第二个回答中试图解释的内容进行了详细阐述

目前的问题是,您的问题意味着您有一个“类引用”(如TClass或TComponentClass),它引用了一个具有虚拟构造函数的基类。 但是,TClass没有(TClass引用具有非虚拟构造函数的类),但TComponentClass有

在使用类引用反汇编对构造函数的调用时,您会看到不同之处。 通过类引用调用虚拟构造函数时,代码与调用非虚拟构造函数时略有不同:

procedure Test;
var
  Clazz: TClass;
  Instance: TObject;
begin
  Clazz := TMyClass;
  Instance := Clazz.Create;
end;
  • 调用虚拟构造函数具有间接性
  • 调用非虚拟构造函数会执行直接调用
这个分解说明了我的意思:

TestingForVirtualConstructor.dpr.37: ComponentClassReference := TMyComponentClass;
00416EEC A1706D4100       mov eax,[$00416d70]
TestingForVirtualConstructor.dpr.38: Instance := ComponentClassReference.Create(nil); // virtual constructor
00416EF1 33C9             xor ecx,ecx
00416EF3 B201             mov dl,$01
00416EF5 FF502C           call dword ptr [eax+$2c]
TestingForVirtualConstructor.dpr.39: Instance.Free;
00416EF8 E8CFCDFEFF       call TObject.Free
TestingForVirtualConstructor.dpr.41: ClassReference := TMyClass;
00416EFD A1946E4100       mov eax,[$00416e94]
TestingForVirtualConstructor.dpr.42: Instance := ClassReference.Create(); // non-virtual constructor
00416F02 B201             mov dl,$01
00416F04 E893CDFEFF       call TObject.Create
TestingForVirtualConstructor.dpr.43: Instance.Free;
00416F09 E8BECDFEFF       call TObject.Free
因此,当您有一个类引用类型的变量,其构造函数是虚拟的,并且您通过该变量调用该构造函数时,您可以确保该变量中的实际类将有一个虚拟构造函数

您无法确定构造函数是在哪个实际类上实现的(当然,没有额外的调试信息,例如来自.DCU、.MAP、.JDBG或其他源的信息)

下面是编译的示例代码:

program TestingForVirtualConstructor;

{$APPTYPE CONSOLE}

uses
  Classes, SysUtils;

type
  TMyComponentClass = class(TComponent)
    MyStrings: TStrings;
    constructor Create(Owner: TComponent); override;
  end;

constructor TMyComponentClass.Create(Owner: TComponent);
begin
  inherited;
end;

type
  TMyClass = class(TObject)
    MyStrings: TStrings;
    constructor Create();
  end;

constructor TMyClass.Create();
begin
  inherited;
end;

procedure Test;
var
  // TComponentClass has a virtual constructor
  ComponentClassReference: TComponentClass;
  ClassReference: TClass;
  Instance: TObject;
begin
  ComponentClassReference := TMyComponentClass;
  Instance := ComponentClassReference.Create(nil); // virtual constructor
  Instance.Free;

  ClassReference := TMyClass;
  Instance := ClassReference.Create(); // non-virtual constructor
  Instance.Free;
end;

begin
  try
    Test;
  except
    on E: Exception do
      Writeln(E.Classname, ': ', E.Message);
  end;
end.
回到你原来的问题: 当类引用引用具有虚拟构造函数的基类时,您可以确保始终使用间接方法调用虚拟构造函数。 当类引用引用具有非虚拟构造函数的基类时,您可以确保始终使用直接调用调用非虚拟构造函数

希望这能为你的问题提供更多的线索


--jeroen

我是否正确理解,如果声明为虚拟的构造函数,将在VMT中引用?是的,与任何其他方法一样……如果您需要此信息,那么我认为您做错了……是的,没错,我想检查其他人的代码中是否有错误。如果构造函数不是声明为虚拟的,它将不会被调用,因此“真正的坏事情可能会发生”。是的,这正是我在第二篇文章中试图表达的观点。很好的解释。当您在ASM中进行说明时,这些事情总是更有意义(至少对于了解实际情况的程序员来说)+1.谢谢。总是很难得到一个既有效又能很好解释的简明示例:-)