Страница 1 из 1

создать новый экземпляр класса по объекту класса

СообщениеДобавлено: 27.03.2019 11:20:00
vkhacker
Здравствуйте!
Требуется создать новый экземпляр класса в функции, которая получает в качестве параметра объект того же класса. Сам класс заранее не известен.

Вот пример, который я создал, чтобы было понятней:

Код: Выделить всё
program sample;

{$mode objfpc}{$H+}

uses
  {$IFDEF UNIX}{$IFDEF UseCThreads}
  cthreads,
  {$ENDIF}{$ENDIF}
  Classes;

type

  { TSampleAbstractClass }

  TSampleAbstractClass = class abstract (TObject)
    private
    Fparam: string;
    public
    constructor Create(const param: string);
  end;

  { TSampleClass }

  TSampleClass = class(TSampleAbstractClass)
  private
    Fobj: TObject;
  public
    constructor Create;
    destructor Destroy; override;
  published
    property obj: TObject read Fobj;
  end;

var
  sample,sample2: TSampleClass;

{ TSampleClass }

constructor TSampleClass.Create;
begin
  Fobj := TObject.Create;
  inherited Create('test');
end;

destructor TSampleClass.Destroy;
begin
  Fobj.Free;
  inherited Destroy;
end;

{ TSampleAbstractClass }

constructor TSampleAbstractClass.Create(const param: string);
begin
  Fparam := param;
end;


function test(const obj: TSampleAbstractClass): TSampleAbstractClass;
begin
  // здесь нужно создать новый объект класса наследника от TSampleAbstractClass
  Result := TSampleAbstractClass(obj.ClassType.Create);
end;

begin
  sample := TSampleClass.Create;

  sample2 := TSampleClass(test(sample));

  if not Assigned(sample2.obj) then
   WriteLn('sample2.obj is not assigned')
else
   WriteLn('sample2.obj is assigned');

  sample2.Free;
  sample.Free;

  ReadLn;
end.


В моем примере вызывается конструктор, как я понял, класса TObject, а не того класса, который должен.

Re: создать новый экземпляр класса по объекту класса

СообщениеДобавлено: 27.03.2019 11:40:39
Дож
Это невозможно.

Re: создать новый экземпляр класса по объекту класса

СообщениеДобавлено: 27.03.2019 11:52:37
vkhacker
Жаль. Придется идти через абстрактные методы похоже...

Re: создать новый экземпляр класса по объекту класса

СообщениеДобавлено: 27.03.2019 12:34:39
Дож
Я подумал чуть по-лучше: если у вас объект-образец является наследником вашего базового класса, и данные из образца в дубликат копировать не нужно, то это можно сделать через виртуальный конструктор:
Код: Выделить всё
{$MODE OBJFPC}
type
TSome = class
  constructor CreateSome; virtual;
end;

TSomeClass = class of TSome;

TOther = class(TSome)
  constructor CreateSome; override;
end;

constructor TSome.CreateSome;
begin
  Writeln('TSome.CreateSome');
end;

constructor TOther.CreateSome;
begin
  Writeln('TOther.CreateSome');
end;

function CreateSame(Obj: TSome): TSome;
begin
  Result := TSomeClass(Obj.ClassType).CreateSome;
end;

begin
  CreateSame(TSome.CreateSome);
  CreateSame(TOther.CreateSome);
end.

Re: создать новый экземпляр класса по объекту класса

СообщениеДобавлено: 27.03.2019 13:29:16
zub
вы хотели сказать
>>создать новый экземпляр класса по экземпляру класса

Я не пробовал, но ИМХО все необходимое есть в вмт, за исключением самого конструктора. создание экземпляра это следующие действия:

NewInstance
InitInstance
Create
AfterConstruction

1,2,4 берем из вмт, 3 организуем предварительной регистрацией возможных классов в структуре наподобии tmap - ключ=адрес вмт, данные=адрес конструктора

Но т.к. у вас есть базовый класс - проще обойтись виртуальными конструкторами

Re: создать новый экземпляр класса по объекту класса

СообщениеДобавлено: 28.03.2019 10:38:18
serbod
1. Можно передавать в функцию не экземпляр, а тип класса и от него рожать. Пример такого можно посмотреть в TCollection.

2. Можно получить тип класса через метод ClassType() и родить от него.

А зачем вам такое извращение? Может проще изменить подход?

Re: создать новый экземпляр класса по объекту класса

СообщениеДобавлено: 28.03.2019 14:40:09
Ichthyander
Не уверен на 100%, но попробуйте добавить функцию класса, которая возвращает класс экземпляра и используйте для этого self. В процедурах и функциях класса self указывает не на экземпляр, а на класс экземпляра. Добавив Create Вы сможете реализовать задуманное

Код: Выделить всё
  { TSampleAbstractClass }

  TSampleAbstractClass = class abstract (TObject)
    private
    Fparam: string;
    public
      constructor Create(const param: string);
      class function SampleClass: TSampleAbstarctClass;
  end;

class function SampleClass: TSampleAbstarctClass;
begin
  Result:=self;
end;

... ...

ASample2:=ASample1.SampleClass.Create(param);



Отмечу, что self это текущий класс объекта, а не базовый класс-родитель TSampleAbstractClass

Добавлено спустя 6 минут 14 секунд:
serbod писал(а):А зачем вам такое извращение? Может проще изменить подход?

К примеру, нужно создать экземпляры того объекта, класс которого неизвестен заранее. Более того, класс может переопределить пользователь библиотеки/класса. При этом пользователю библиотеки/класса нет необходимости залезать в реализацию этой библиотеки/класса - он просто добавляет свою реализацию/наследник класса TSampleAbstractClass и подключает его к библиотеки, к примеру, добавив процедуру регистрации класса

Re: создать новый экземпляр класса по объекту класса

СообщениеДобавлено: 28.03.2019 15:11:14
Снег Север
Ichthyander писал(а):К примеру, нужно создать экземпляры того объекта, класс которого неизвестен заранее.

По-моему это грубое нарушение самого принципа объектного программирования.

Re: создать новый экземпляр класса по объекту класса

СообщениеДобавлено: 28.03.2019 15:18:01
Ichthyander
Снег Север писал(а):
Ichthyander писал(а):К примеру, нужно создать экземпляры того объекта, класс которого неизвестен заранее.

По-моему это грубое нарушение самого принципа объектного программирования.

Не думаю. Это очень удобно именно как объектно-ориентированное программирование. Это даже не хак и не хитрость и не deprecated - вполне нормально использование, пусть и редко используемое, так как такая задача не так часто возникает и достаточно специфична

Добавлено спустя 2 минуты 29 секунд:
Уточню, что "не то чтобы" класс неизвестен. А неизвестна реализация базового класса (то есть какой наследник класса был использован для объекта донора для клона). И указанный путь вполне себе красивое решение и не нарушает никакие принципы )

Re: создать новый экземпляр класса по объекту класса

СообщениеДобавлено: 28.03.2019 15:50:46
alexs
виртуальный конструктор + виртуальный метод для копирования значения свойств (аналог Assign из TComponent)
Код: Выделить всё
    V1:=V.ClassType.Create;
    if Assigned(V1) then
    begin
      if V1 is TSQLCommandAbstract then
      begin
        TSQLCommandAbstract(V1).Create(nil);
        try
          TSQLCommandDDL(V1).Assign(V);
        finally

Вот часть моего кода

Re: создать новый экземпляр класса по объекту класса

СообщениеДобавлено: 31.03.2019 10:23:11
DedFrend
Не понимаю почему никто не упоминает методы Assign и AssignTo, которые как раз для этого предназначены. Но, конечно, надо потрудится и сделать их реализации для всех классов, которые собираетесь клонировать.

Re: создать новый экземпляр класса по объекту класса

СообщениеДобавлено: 31.03.2019 11:13:52
Лекс Айрин
DedFrend, потому что они для этого не предназначены. Я уже попался на это, когда попытался подключить канвас к компоненту. Заодно научился рисовать на экране монитора. Assign позволяет получить доступ к объекту.

Re: создать новый экземпляр класса по объекту класса

СообщениеДобавлено: 31.03.2019 12:14:20
DedFrend
А я использую, и успешно, правда не слишком широко

Re: создать новый экземпляр класса по объекту класса

СообщениеДобавлено: 02.04.2019 11:46:11
Ichthyander
DedFrend писал(а):Не понимаю почему никто не упоминает методы Assign и AssignTo, которые как раз для этого предназначены. Но, конечно, надо потрудится и сделать их реализации для всех классов, которые собираетесь клонировать.

Потому что это не имеет отношения к поставленной задаче. Он не спрашивал как скопировать свойства и поля объекта вновь созданному. Он спрашивал как создать объект заданного класса (неизвестно какого именно на стадии компиляции).