Форум: "Основная";
Текущий архив: 2005.11.27;
Скачать: [xml.tar.bz2];
ВнизВиртуальное клонирование Найти похожие ветки
← →
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 вся ветка
Форум: "Основная";
Текущий архив: 2005.11.27;
Скачать: [xml.tar.bz2];
Память: 0.51 MB
Время: 0.016 c