Вниз
Скачать: CL | DM;

Виртуальное клонирование   Найти похожие ветки 

← →
JLes ©   (2005-10-31 16:33) [0]

Глубокоуважаемый All!

Есть иерархия классов, в основе которой лежит некоторый класс TMyObject. Есть также TMyList, в котором лежат объекты классов из вышеуказанной иерархии, доступные через Items[idx]: TMyObject. В TMyList имеется также метод, позволяющий копировать часть одного списка в другой.

Вопрос - как эффективнее всего (как с точки зрения выполнения, так и с точки зрения программирования и сопровождения) организовать создание копий объектов TMyObject (и потомков)?


← →
Игорь Шевченко ©   (2005-10-31 16:36) [1]


> Вопрос - как эффективнее всего (как с точки зрения выполнения,
>  так и с точки зрения программирования и сопровождения)
> организовать создание копий объектов TMyObject (и потомков)?
>


Написать виртуальный метод Assign, в котором присваивать значения свойств исходного объекта вновь созданному.

Образцов такого рода действий много в исходных кодах VCL.


← →
JLes ©   (2005-10-31 17:24) [2]


> Написать виртуальный метод Assign, в котором присваивать
> значения свойств исходного объекта вновь созданному.


Гм. Представим:


procedure TMyList.CopyFragment(idxBegin, idxEnd: integer; Source: TMyList);
var
 src, tmp: TMyObject;
 i: integer;
begin
 for i:=idxBegin to idxEnd do
 begin
   src:=Source.Items[i];
   tmp:=??? // создаем объект класса, совпадающего с классом src
   tmp.Assign(src); // вариант 1 будет работать, если правильно сработает
// предыдущая строчка. Иначе будет неизвестно, Assign какого класса вызывать.
// однако src будет правильного класса

   src.AssignTo(tmp); // вариант2 - будет вызван Assign правильного класса,
// однако это все равно будет работать только в том случае, если tmp
// будет того же класса, что и src.

 end;
end;


Так что все упирается в создание объекта нужного класса, когда носителем информации о классе является сам копируемый объект. Решение в лоб:


if src is TMyObject2 then
 tmp:=TMyObject2.Create
else if src is TMyObject3 then
 tmp:=TMyObject3.Create
...


Assign сам по себе, без корректного создания копии - не решение.


← →
reonid ©   (2005-10-31 17:33) [3]

TMyBaseObject = class
 ...
 function Clone: TMyBaseObject; virtual;
end;


← →
JLes ©   (2005-10-31 18:27) [4]


> TMyBaseObject = class
>  ...
>  function Clone: TMyBaseObject; virtual;
> end;


Была такая идея. Неприятно то, что ее нужно писать для каждого класса-наследника, хотя везде она реализуется по одному образцу:

Result:=<нужное имя класса вписать>.Create;
Result:=Assign(Self);

Хотяя.....

Если сделать виртуальные конструктор и Assign, но саму функцию Clone - не виртуальной и только в базовом классе...

Ща попробую.


← →
Набережных С. ©   (2005-10-31 19:28) [5]

TObject.ClassType плюс приведение к базовому классу ?

type
 TMyObj = class
 end;

procedure TForm1.Button1Click(Sender: TObject);
var
 a, b: TObject;
begin
 a:=TMyObj.Create;
 b:=a.ClassType.Create;
 ShowMessage(a.ClassName);
 ShowMessage(b.ClassName);
 a.Free;
 b.Free;
end;


← →
Набережных С. ©   (2005-10-31 19:36) [6]

Типа такого:

type
 TMyObj = class
   procedure Assign(Source: TMyObj);
   function Clone: TMyObj;
 end;

TMyObjClass = class of TMyObj;

procedure TMyObj.Assign(Source: TMyObj);
begin
...
end;

function TMyObj.Clone: TMyObj;
begin
 Result:=TMyObjClass(ClassType).Create;
 Result.Assign(Self);
end;


?


← →
Набережных С. ©   (2005-10-31 19:42) [7]

Да, забыл - Assign и конструктор разумеется виртуальные.
Сам я не пробовал, но вроде бы должно получиться.


← →
JLes ©   (2005-10-31 20:02) [8]


>  Result:=TMyObjClass(ClassType).Create;


Как раз приведение типа здесь может оказаться нерабочим, если это приведение достанет статическую ссылку на конструктор. Если же это приведение нужно просто для получения таблицы виртуальных методов, то должно сработать.


← →
Набережных С. ©   (2005-10-31 20:13) [9]


> JLes ©   (31.10.05 20:02) [8]

см. [7]:

> Assign и конструктор разумеется виртуальные


← →
Набережных С. ©   (2005-10-31 20:21) [10]

А для и статических, и виртуальных конструкторов можно попробовать так:

Result:=TMyObj(ClassType.Create);


← →
reonid ©   (2005-10-31 20:27) [11]

> Набережных С. ©   (31.10.05 20:21) [10]
> Result:=TMyObj(ClassType.Create);

тогда вызовется конструктор TObject.Create.
Мне кажется, это не совсем то, что нужно...


← →
Набережных С. ©   (2005-10-31 20:59) [12]


> reonid ©   (31.10.05 20:27) [11]

Разве? В  [5] вроде вызывается конструктор TMyObj. Почему должно быть иначе при TMyObj(ClassType.Create)?


← →
Набережных С. ©   (2005-10-31 21:02) [13]


> reonid ©   (31.10.05 20:27) [11]

Мы ведь так обращаемся к конкретному классу конкретного объекта. Т.е. вызываем конструктор именно того класса, к которому принадлежит данный конкретный экзэмплер объекта. Такого-же класса объект и будет создеан, имхо. Проверять лень, честно говоря:))


← →
umbra ©   (2005-10-31 21:06) [14]


type

TMyObjClass = class of TMyObj;

TMyObj = class
  procedure Assign(Source: TMyObj);
  function Clone(src: TMyObjClass): TMyObj;
end;

procedure TMyObj.Assign(Source: TMyObj);
begin
...
end;

function TMyObj.Clone(src: TMyObjClass): TMyObj;
begin
Result:=src.Create;
Result.Assign(Self);
end;



← →
Набережных С. ©   (2005-10-31 21:38) [15]


> umbra ©   (31.10.05 21:06) [14]

По условию задачи, имеется массив различных потомков некоего базового класса. В свете этого пример вызова Clone(src: TMyObjClass) можно увидеть?


← →
Набережных С. ©   (2005-10-31 21:44) [16]

Ладно, я спать. Что будет, завтра дочитаю:)
Всем доброй ночи.


← →
Lamer@fools.ua ©   (2005-10-31 22:30) [17]

>>JLes ©   (31.10.05 17:24) [2]

src:=Source.Items[i];
  tmp:=??? // создаем объект класса, совпадающего с классом src
  tmp.Assign(src); // вариант 1 будет работать, если правильно сработает
// предыдущая строчка. Иначе будет неизвестно, Assign какого класса вызывать.
// однако src будет правильного класса


src:=Source.Items[i];
tmp := TMyObject(src.NewInstance);
try
  tmp.Create(...);
  tmp.Assign(src);
except
  tmp.FreeInstance;
end;


Конструктор Create и метод Assign, как уже писали ранее, должны быть виртуальными.


← →
reonid ©   (2005-10-31 23:46) [18]

>Набережных С. ©   (31.10.05 20:21) [10]

ClassType возвращает значение типа TClass.
Ему доступен только интерфейс TObject.

Если нужно вызвать методы своего класса,
нужно снабдить компилятор соответствующей
статической информацией,
т.е. привести TClass к TMyObjectClass.
Тогда для TMyObjectClass(myObj.ClassType)
будет доступен интерфейс класса TMyObject.
Соответственно, чтобы этот интерфейс был актуален
и для классов-потомков TMyObject, он должен иметь
минимум виртуальный конструктор.


← →
Набережных С. ©   (2005-11-01 07:17) [19]


> reonid ©   (31.10.05 23:46) [18]

Все-таки вынудили проверить:)
Да, верно, в случае статического конструктора это не проходит.
Зато с виртуальным конструктором, как предлагалось изначально в [6], [7], все отлично работает, даже если Clone - статический.

type
 TMyBase = class
 private
   FID: integer;
 protected
   procedure ShowID;
   function Clone: TMyBase;
 public
   constructor Create; virtual; abstract;
 end;

 TMyBaseClass = class of TMyBase;
 
 TMyObj1 = class(TMyBase)
 public
   constructor Create; override;
 end;

 TMyObj2 = class(TMyBase)
 public
   constructor Create; override;
 end;

 TMyObj3 = class(TMyObj1)
 public
   constructor Create; override;
 end;

{ TMyBase }

function TMyBase.Clone: TMyBase;
begin
 Result:=TMyBaseClass(ClassType).Create;
end;

procedure TMyBase.ShowID;
begin
 ShowMessageFmt("Class: %s; ID: %d", [ClassName, FID]);
end;

{ TMyObj1 }

constructor TMyObj1.Create;
begin
 FID:=1;
end;

{ TMyObj2 }

constructor TMyObj2.Create;
begin
 FID:=2;
end;

{ TMyObj3 }

constructor TMyObj3.Create;
begin
 FID:=3;
end;

{ TForm1 }

procedure TForm1.Button1Click(Sender: TObject);
var
S1, S2, S3: TMyBase;
C1, C2, C3: TMyBase;
begin
 S1:=nil; S2:=nil; S3:=nil;
 C1:=nil; C2:=nil; C3:=nil;
 try
   S1:=TMyObj1.Create;
   S2:=TMyObj2.Create;
   S3:=TMyObj3.Create;
   C1:=S1.Clone;
   C2:=S2.Clone;
   C3:=S3.Clone;
   C1.ShowID;
   C2.ShowID;
   C3.ShowID;
 finally
   S1.Free; S2.Free; S3.Free;
   C1.Free; C2.Free; C3.Free;
 end;
end;


← →
Игорь Шевченко ©   (2005-11-01 10:30) [20]

А какой смысл баталии разводить - достаточно посмотреть, как форма загружается из ресурса dfm - там все вроде ясно написано :)


← →
Lamer@fools.ua ©   (2005-11-01 10:57) [21]

Обманул немного в [17]

src:=Source.Items[i];
tmp := TMyObject(src.NewInstance);
tmp.Create(...);
try
   tmp.Assign(src);
except
   tmp.Free;
   raise;
end;


← →
Набережных С. ©   (2005-11-01 11:42) [22]


> Игорь Шевченко ©   (01.11.05 10:30) [20]

Имхо, в данном случая регистрация класса несколько лишнее, зачем код увеличивать, раз без него можно обойтись? При загрузки формы нет образца для копирования, отсюда и необходимость в регистрации/поиске класса. А здесь оно как бы без надобности.

> Lamer@fools.ua ©   (01.11.05 10:57) [21]

Имхо то же самое, что в [6] и [19], с тем же ограничением -  необходим виртуальный конструктор, только кода на одну строку больше:))

А вообще-то Игорь прав. Автор вопроса давно получил решение и наплевал и на нас, и на ветку, а мы тут распинаемся...Посему я откланиваюсь.


← →
JLes ©   (2005-11-02 10:09) [23]


> Автор вопроса давно получил решение
> и наплевал и на нас, и на ветку, а мы тут распинаемся...


Неправда. Просто у меня во вторник тырнета не было.

Для меня ключевым моментом было использование ClassType и приведение его к корневому классу иерархии. После этого все было делом времени.



Страницы: 1 вся ветка

Скачать: CL | DM;



Память: 0.51 MB
Время: 0.027 c
4-1127742005
NikNet
2005-09-26 17:40
2005.11.27
Где взять компонент типа TaskBar


14-1131286082
_xroot
2005-11-06 17:08
2005.11.27
ThemeEngine 5.52


6-1124024764
NikNet
2005-08-14 17:06
2005.11.27
Как отправить/принять через WinSock свыше 1gb?


2-1131599978
paule
2005-11-10 08:19
2005.11.27
Перенос строки в Memo


2-1131605582
начиннающий
2005-11-10 09:53
2005.11.27
DynamicSkinForm




   Наверх