Могу ли я клонировать объект, скопировав его память? - PullRequest
8 голосов
/ 17 октября 2011

Мне нужно иметь стек отмены + повторения для ограниченного числа классов под моим контролем, который должен быть очень-очень быстрым, а использование RTTI и XML или потоков невозможно, так как количество экземпляров может достигать 2000+во вложенных списках объектов.Объекты должны быть скопированы в шаблон памяти и из него и загружены немедленно.

Существует ли способ клонирования объектов путем копирования памяти и повторного создания объектов из этой памяти?

Ответы [ 5 ]

15 голосов
/ 17 октября 2011

Едва ли.Вы можете легко скопировать память объекта, но часть этой памяти будет указателем, и в этом случае вы только копируете ссылку.Эти указатели могут включать строки и другие объекты.

Я думаю, что лучший способ - это наследовать эти классы от TPersistent (или любого другого потомка) и реализовать метод Assign для каждого из них.Таким образом, вы можете создать второй экземпляр и назначить объект этому новому экземпляру.В реализации Assign вы можете сами решить, какие данные следует копировать, а какие нет.

11 голосов
/ 18 октября 2011

2000 + вложенные объекты не так велики и не будут такими медленными, даже с RTTI (доступ к диску или сжатие будут намного медленнее).При прямой ручной сериализации SaveToStream это очень быстро, если вы используете FastMM4 (диспетчер памяти по умолчанию с Delphi 2006).

Возможно, вы можете изменить свой алгоритм и использовать вместо него динамические массивы (есть сериализатор с открытым исходным кодом здесь ).Но ваши данные могут не подходить для таких записей.

Или никогда / редко освобождать память, а использовать только объекты или ссылки на записи.Вы можете иметь пул объектов в памяти, с каким-то ручным сборщиком мусора и обрабатывать только ссылки на объекты (или записи).

Если у вас есть string внутри объектов, вы не можете перераспределятьих и поддерживать глобальный список используемых строк: подсчет ссылок и копирование при записи сделают это намного быстрее, чем стандартная сериализация и распределение (даже с FastMM4).

Во всех случаях стоит сделать реальныйпрофилирование вашего приложения.Общее человеческое предположение о узких местах производительности в большинстве случаев неверно.Доверяйте только профилировщику и настенным часам.Возможно, ваша текущая реализация не такая медленная, и реальным узким местом является не объектный процесс, а где-то еще.

Не оптимизируйте слишком рано.«Сделайте это правильно, прежде чем делать это быстро. Проясните это, прежде чем делать это быстрее. Держите это правильно, когда вы делаете это быстрее».- Керниган и Плаугер, Элементы стиля программирования.

3 голосов
/ 17 октября 2011

Простой способ клонировать объект:

  • Получите ваш клонируемый класс из TPersistent
  • Реализация Назначить процедуру

См. Пример шаблона дизайна Memento здесь: http://sourcemaking.com/design_patterns/memento/delphi/1

1 голос
/ 18 октября 2011

Подход, который я использовал для этой ситуации в прошлом, состоит в том, чтобы объявить запись с частью объекта, которую мне нужно сохранить, и использовать эту запись в классе. Смотрите мой старый ответ на Оптимизация размера класса в Delphi. Есть ли что-то вроде "упакованных классов"? для примера.

Поскольку записи копируются при присваивании, тогда легко скопировать поля из одного типа объекта в другой.

, например

TMyFields = record
  ID: Integer;
  Name: string;
end;

TMyClass = class(TPersistent)    
 private
  FFields: TMyFields;
  FStack: TStack;
  class TStackItem = class(TObject)
  public
    Fields: TMyFields;
  end;

protected
   property ID: integer read FFields.ID;
   property Name: string read FFields.Name;
   procedure Push;
   // etc...
end;


procedure TMyClass.Push;
var
  NewItem: TStackItem;
begin
  NewItem := TStackItem.Create;
  NewItem.Fields := FFields;
  FStack.Push(NewItem);
end;

Конечно, поскольку вы используете XE, вы можете использовать Generics.Collections.TObjectStack .

0 голосов
/ 17 октября 2011

Undo / Redo может быть реализовано с TMemoryStream - просто сохраните данные объекта в поток и загрузите их, когда появится redo / undo. Он использует объектный механизм saveToStream / loadfromstream и позволяет правильно восстанавливать ссылки.

Обновление:

TMyObject = class
    procedure SaveToStream(AStream : TStream);
    procedure LoadFromStream(AStream : TStream);
end;

Тогда:

До изменения:

myobject.SaveToStream(MemoryStream);
undoManager.AddItem(myobject.Identifier, MemoryStream);
Do change object...

По восстановлению

myobject := FindObject(undomanager.LastItem.Identifier)
myobject.LoadFromStream(undomanager.LastItem.MemoryStream);

Я использую аналогичный подход для своего программного обеспечения САПР, он работает довольно хорошо. Он также может быть использован для хранения нескольких объектов, измененных за одну операцию.

...