我有以下超类:
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,即使它没有出现在ShowForm中。
我在以下行获得了EAbstractError:
unit Forms;
(...)
constructor TCustomForm.Create(AOwner: TComponent);
begin
(...)
InitializeNewForm; //EAbstractError
(...)
end;
我做错了什么?
编辑:这当然不是我的确切代码。
答案 0 :(得分:8)
Delphi构造函数采用隐藏的额外参数来指示两件事:是否需要调用NewInstance
,以及隐式第一个参数(Self
)的类型是什么。从类或类引用中调用构造函数时,实际上需要构造一个新对象,Self
参数的类型将是实际的类类型。从另一个构造函数调用构造函数时,或者在调用继承的构造函数时,已经创建了对象实例并将其作为Self
参数传递。隐藏的额外参数充当Boolean
标志,True
用于分配新实例,False
用于构造函数的方法样式调用。
因此,您不能简单地将构造函数存储在方法指针[1]位置并期望它能够工作;调用方法指针不会传递隐藏的额外参数的正确值,它会中断。你可以通过明确声明参数并进行类型转换来解决它。但通常直接使用元类(类引用)更为可取且不易出错。
[1]这是你的代码的另一个问题。您正在尝试将方法指针存储在函数指针位置。您可以这样做并仍然使其工作,但您需要明确地将Self
的声明置于其中,并且您还需要在分配时传递元类作为第一个参数(以及传递对于隐式标志为真)。方法指针在第一个参数中烘焙并自动传递。为了使其全部显式化,等效于TComponent.Create
的函数指针类似于:
TComponentCreate = function(Self: Pointer; AOwner: TComponent; DoAlloc: Boolean): Pointer;
Self
是指针,因为它可能是TComponentClass
类型或TComponent
类型,具体取决于DoAlloc
是真还是假。
答案 1 :(得分:2)
您没有正确使用虚拟构造函数。试试这样:
type
TDefaultFormDlgClass = class of TDefaultFormDlg;
function Show(FormClass: TDefaultFormDlgClass; AOwner: TComponent): TDefaultFormDlg;
begin
Result := FormClass.Create(AOwner);
end;
...
var
FormClass: TTDefaultFormDlgClass;
...
FormClass := ???;//this is where you specify the class at runtime, e.g. TForm1
MyForm := Show(FormClass, MainForm);
顺便说一下,我认为您不需要在列出的代码中重新引入构造函数。
答案 2 :(得分:2)
根据Barry的信息我测试了这段代码。 TSample是一个带有无框架构造函数的简单类。您只需要指向构造函数(@ TSample.Create)的指针和类的类型(TSample)。如果你有一个hashmap key = TClass,value = Pointer ctor你可以创建任何注册类型。
type
TObjectCreate = function(Self: TClass; DoAlloc: Boolean): TObject;
var
aSample : TSample;
begin
aSample := TSample(TObjectCreate(@TSample.Create)(TSample, true));