将指针传递给构造函数作为参数的问题

时间:2011-06-12 11:09:01

标签: delphi delphi-2009

我有以下超类:

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);
发生了两件事:

  1. 当我进入TForm1.Create时,AOwner = nil,即使它没有出现在ShowForm中。

  2. 我在以下行获得了EAbstractError:

    unit Forms;
    (...)
    constructor TCustomForm.Create(AOwner: TComponent);
    begin
      (...)
      InitializeNewForm; //EAbstractError
      (...)
    end;
    
  3. 我做错了什么?

    编辑:这当然不是我的确切代码。

3 个答案:

答案 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));