将指针作为参数传递给构造函数时出现问题
我有以下超类:
unit DlgDefaultForm;
type
TDefaultFormDlg = class(TForm)
published
constructor Create(AOwner: TComponent); reintroduce; virtual;
end;
FormCreateFunc=function(AOwner: TComponent):TDefaultFormDlg;
它是由一堆表单派生的,如下所示:
unit Form1
type
TForm1 = class(TDefaultFormDlg)
published
constructor Create(AOwner: TComponent); override;
end;
并创建如下:
unit MainForm;
procedure ShowForm(FormCreate:FormCreateFunc);
begin
(do some stuff)
FormCreate(ScrollBox1);
end;
当我运行时,
ShowForm(@TForm1.Create);
会发生两件事:
当我进入 TForm1.Create 时,AOwner=nil,即使它没有 当我进入TForm1.Create
我在以下行收到 EAbstractError:
单位表格; (...) 构造函数 TCustomForm.Create(AOwner: TComponent); 开始 (...) 初始化新表单; //E抽象错误 (...) 结尾;
我做错了什么?
编辑:这当然不是我的确切代码。
I have the following superclass:
unit DlgDefaultForm;
type
TDefaultFormDlg = class(TForm)
published
constructor Create(AOwner: TComponent); reintroduce; virtual;
end;
FormCreateFunc=function(AOwner: TComponent):TDefaultFormDlg;
which is descended by a bunch of forms as follows:
unit Form1
type
TForm1 = class(TDefaultFormDlg)
published
constructor Create(AOwner: TComponent); override;
end;
and created as follows:
unit MainForm;
procedure ShowForm(FormCreate:FormCreateFunc);
begin
(do some stuff)
FormCreate(ScrollBox1);
end;
When I run
ShowForm(@TForm1.Create);
two things happen:
When I step into TForm1.Create, AOwner=nil, even when it didn't in ShowForm.
I get an EAbstractError at the following line:
unit Forms; (...) constructor TCustomForm.Create(AOwner: TComponent); begin (...) InitializeNewForm; //EAbstractError (...) end;
What am I doing wrong?
EDIT: This of course isn't my exact code.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
Delphi 构造函数采用一个隐藏的额外参数,该参数指示两件事:是否需要调用
NewInstance
以及隐式第一个参数 (Self
) 的类型。当您从类或类引用调用构造函数时,您实际上想要构造一个新对象,并且Self
参数的类型将是实际的类类型。当您从另一个构造函数调用构造函数时,或者当您调用继承的构造函数时,对象实例已经创建并作为Self
参数传递。隐藏的额外参数充当Boolean
标志,对于分配新实例,该标志为True
;对于构造函数的方法样式调用,则为False
。因此,您不能简单地将构造函数存储在方法指针 [1] 位置并期望它能够工作;调用方法指针不会为隐藏的额外参数传递正确的值,并且它会中断。您可以通过显式声明参数并进行一些类型转换来解决这个问题。但通常直接使用元类(类引用)更可取且更不易出错。
[1] 这是您的代码的另一个问题。您试图将方法指针存储在函数指针位置。您可以这样做并仍然使其工作,但是您需要显式地声明
Self
,并且您还需要在分配时将元类作为第一个参数传递(如以及为隐式标志传递 True)。方法指针烘焙第一个参数并自动传递它。为了使其全部明确,与TComponent.Create
等效的函数指针类似于:Self
在这里是一个指针,因为它可以是TComponentClass
类型,或TComponent
类型,具体取决于DoAlloc
是 true 还是 false。Delphi constructors take a hidden extra parameter which indicates two things: whether
NewInstance
needs to be called, and what the type of the implicit first parameter (Self
) is. When you call a constructor from a class or class reference, you actually want to construct a new object, and the type of theSelf
parameter will be the actual class type. When you call a constructor from another constructor, or when you're calling the inherited constructor, then the object instance has already been created and is passed as theSelf
parameter. The hidden extra parameter acts as aBoolean
flag which isTrue
for allocating a new instance butFalse
for method-style calls of the constructor.Because of this, you can't simply store a constructor in a method pointer[1] location and expect it to work; calling the method pointer won't pass the correct value for the hidden extra parameter, and it will break. You can get around it by declaring the parameter explicitly, and doing some typecasting. But usually it is more desirable and less error-prone to use metaclasses (class references) directly.
[1] That's another problem with your code. You're trying to store a method pointer in a function pointer location. You could do that and still make it work, but you'd need to put the declaration of
Self
in explicitly then, and you'd also need to pass the metaclass as the first parameter when allocating (as well as passing True for the implicit flag). Method pointers bake in the first parameter and pass it automatically. To make it all explicit, the function pointer equivalent toTComponent.Create
is something like:Self
is a pointer here because it could be ofTComponentClass
type, orTComponent
type, depending on whetherDoAlloc
is true or false.您没有正确使用虚拟构造函数。尝试这样:
顺便说一句,我认为您不需要在列出的代码中重新引入构造函数。
You're not using virtual constructors correctly. Try it like this:
As an aside I do not think you need to reintroduce the constructor in the code you listed.
根据巴里的信息,我测试了这段代码。 TSample 是一个带有无参数构造函数的简单类。您所需要的只是指向构造函数的指针 (@TSample.Create) 和类的类型 (TSample)。如果您有一个 hashmap key=TClass, value=Pointer ctor,您可以创建任何注册类型。
Based on the information from Barry I tested this code. TSample is a simple class with a paramless constructor. All you need is the pointer to the constructor (@TSample.Create) and the type of the class (TSample). If you've a hashmap key=TClass, value=Pointer ctor you can create any registered type.