Проблемы с передачей указателя на конструктор в качестве параметра - PullRequest
2 голосов
/ 12 июня 2011

У меня есть следующий суперкласс:

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 ]

8 голосов
/ 12 июня 2011

Конструкторы 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 истинным или ложным.

2 голосов
/ 01 августа 2014

На основании информации от Барри я проверил этот код. TSample - это простой класс с конструктором без параметров. Все, что вам нужно, это указатель на конструктор (@ TSample.Create) и тип класса (TSample). Если у вас есть ключ hashmap = TClass, значение = указатель ctor, вы можете создать любой зарегистрированный тип.

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

var
  aSample : TSample;
begin
  aSample := TSample(TObjectCreate(@TSample.Create)(TSample, true));
2 голосов
/ 12 июня 2011

Вы не используете виртуальные конструкторы правильно.Попробуйте это так:

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

В качестве отступления я не думаю, что вам нужно повторно вводить конструктор в коде, который вы перечислили.

...