将指针作为参数传递给构造函数时出现问题

发布于 2024-11-14 20:10:01 字数 957 浏览 1 评论 0原文

我有以下超类:

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,即使它没有 当我进入TForm1.Create

  2. 我在以下行收到 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:

  1. When I step into TForm1.Create, AOwner=nil, even when it didn't in ShowForm.

  2. 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

倾城泪 2024-11-21 20:10:01

Delphi 构造函数采用一个隐藏的额外参数,该参数指示两件事:是否需要调用 NewInstance 以及隐式第一个参数 (Self) 的类型。当您从类或类引用调用构造函数时,您实际上想要构造一个新对象,并且 Self 参数的类型将是实际的类类型。当您从另一个构造函数调用构造函数时,或者当您调用继承的构造函数时,对象实例已经创建并作为 Self 参数传递。隐藏的额外参数充当 Boolean 标志,对于分配新实例,该标志为 True;对于构造函数的方法样式调用,则为 False

因此,您不能简单地将构造函数存储在方法指针 [1] 位置并期望它能够工作;调用方法指针不会为隐藏的额外参数传递正确的值,并且它会中断。您可以通过显式声明参数并进行一些类型转换来解决这个问题。但通常直接使用元类(类引用)更可取且更不易出错。

[1] 这是您的代码的另一个问题。您试图将方法指针存储在函数指针位置。您可以这样做并仍然使其工作,但是您需要显式地声明 Self ,并且您还需要在分配时将元类作为第一个参数传递(如以及为隐式标志传递 True)。方法指针烘焙第一个参数并自动传递它。为了使其全部明确,与 TComponent.Create 等效的函数指针类似于:

TComponentCreate = function(Self: Pointer; AOwner: TComponent; DoAlloc: Boolean): Pointer;

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 the Self 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 the Self parameter. The hidden extra parameter acts as a Boolean flag which is True for allocating a new instance but False 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 to TComponent.Create is something like:

TComponentCreate = function(Self: Pointer; AOwner: TComponent; DoAlloc: Boolean): Pointer;

Self is a pointer here because it could be of TComponentClass type, or TComponent type, depending on whether DoAlloc is true or false.

慢慢从新开始 2024-11-21 20:10:01

您没有正确使用虚拟构造函数。尝试这样:

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

顺便说一句,我认为您不需要在列出的代码中重新引入构造函数。

You're not using virtual constructors correctly. Try it like this:

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

As an aside I do not think you need to reintroduce the constructor in the code you listed.

挽清梦 2024-11-21 20:10:01

根据巴里的信息,我测试了这段代码。 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));

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.

type
  TObjectCreate = function(Self: TClass; DoAlloc: Boolean): TObject;

var
  aSample : TSample;
begin
  aSample := TSample(TObjectCreate(@TSample.Create)(TSample, true));
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文