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

Общие вопросы программирования, алгоритмы и т.п.

Модератор: Модераторы

Ответить
vkhacker
незнакомец
Сообщения: 9
Зарегистрирован: 06.02.2014 09:50:35

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

Сообщение 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, а не того класса, который должен.
Аватара пользователя
Дож
энтузиаст
Сообщения: 900
Зарегистрирован: 12.10.2008 16:14:47

Сообщение Дож »

Это невозможно.
vkhacker
незнакомец
Сообщения: 9
Зарегистрирован: 06.02.2014 09:50:35

Сообщение vkhacker »

Жаль. Придется идти через абстрактные методы похоже...
Аватара пользователя
Дож
энтузиаст
Сообщения: 900
Зарегистрирован: 12.10.2008 16:14:47

Сообщение Дож »

Я подумал чуть по-лучше: если у вас объект-образец является наследником вашего базового класса, и данные из образца в дубликат копировать не нужно, то это можно сделать через виртуальный конструктор:

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

{$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.
zub
долгожитель
Сообщения: 2889
Зарегистрирован: 14.11.2005 22:51:26
Контактная информация:

Сообщение zub »

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

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

NewInstance
InitInstance
Create
AfterConstruction

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

Но т.к. у вас есть базовый класс - проще обойтись виртуальными конструкторами
Аватара пользователя
serbod
постоялец
Сообщения: 449
Зарегистрирован: 16.09.2016 10:03:02
Откуда: Минск
Контактная информация:

Сообщение serbod »

1. Можно передавать в функцию не экземпляр, а тип класса и от него рожать. Пример такого можно посмотреть в TCollection.

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

А зачем вам такое извращение? Может проще изменить подход?
Аватара пользователя
Ichthyander
энтузиаст
Сообщения: 701
Зарегистрирован: 04.04.2007 08:32:43
Откуда: Астрахань
Контактная информация:

Сообщение 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 и подключает его к библиотеки, к примеру, добавив процедуру регистрации класса
Аватара пользователя
Снег Север
долгожитель
Сообщения: 3071
Зарегистрирован: 27.11.2007 15:14:47
Контактная информация:

Сообщение Снег Север »

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

По-моему это грубое нарушение самого принципа объектного программирования.
Аватара пользователя
Ichthyander
энтузиаст
Сообщения: 701
Зарегистрирован: 04.04.2007 08:32:43
Откуда: Астрахань
Контактная информация:

Сообщение Ichthyander »

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

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

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

Добавлено спустя 2 минуты 29 секунд:
Уточню, что "не то чтобы" класс неизвестен. А неизвестна реализация базового класса (то есть какой наследник класса был использован для объекта донора для клона). И указанный путь вполне себе красивое решение и не нарушает никакие принципы )
Аватара пользователя
alexs
долгожитель
Сообщения: 4069
Зарегистрирован: 15.05.2005 23:17:07
Откуда: г.Ставрополь
Контактная информация:

Сообщение 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

Вот часть моего кода
DedFrend
постоялец
Сообщения: 157
Зарегистрирован: 25.11.2018 11:21:50

Сообщение DedFrend »

Не понимаю почему никто не упоминает методы Assign и AssignTo, которые как раз для этого предназначены. Но, конечно, надо потрудится и сделать их реализации для всех классов, которые собираетесь клонировать.
Аватара пользователя
Лекс Айрин
долгожитель
Сообщения: 5723
Зарегистрирован: 19.02.2013 16:54:51
Откуда: Волгоград
Контактная информация:

Сообщение Лекс Айрин »

DedFrend, потому что они для этого не предназначены. Я уже попался на это, когда попытался подключить канвас к компоненту. Заодно научился рисовать на экране монитора. Assign позволяет получить доступ к объекту.
DedFrend
постоялец
Сообщения: 157
Зарегистрирован: 25.11.2018 11:21:50

Сообщение DedFrend »

А я использую, и успешно, правда не слишком широко
Аватара пользователя
Ichthyander
энтузиаст
Сообщения: 701
Зарегистрирован: 04.04.2007 08:32:43
Откуда: Астрахань
Контактная информация:

Сообщение Ichthyander »

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

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