我可以通过复制其内存来克隆对象吗?

时间:2011-10-17 18:37:36

标签: delphi design-patterns delphi-xe

我需要在我的控制下为有限数量的类提供undo + redo堆栈,这些类必须非常非常快并且使用RTTI和XML或流是不可行的,因为实例的数量可能高达2000+在嵌套对象列表中。需要通过memento模式复制和移出对象并立即重新加载。

有没有办法通过复制内存并从内存中重新实例化对象来克隆对象?

5 个答案:

答案 0 :(得分:15)

几乎没有。您可以轻松复制对象的内存,但该内存的一部分将是指针,在这种情况下,您只复制引用。这些指针也可以包括字符串和其他对象。

我认为最好的方法是从TPersistent(或任何后代)继承这些类,为每个类实现Assign方法。这样,您可以创建第二个实例并将对象分配给该新实例。在Assign实现中,您可以自己决定应该复制哪些数据以及不应该复制哪些数据。

答案 1 :(得分:11)

2000+嵌套对象不是那么庞大,并且即使使用RTTI也不会那么慢(磁盘访问或压缩会慢得多)。使用直接的SaveToStream手动序列化,如果你使用FastMM4(自Delphi 2006以来的默认内存管理器),它会非常快。

也许您可以更改算法并使用动态数组(有一个开源序列化程序here)。但是你的数据可能不适合这种记录。

或者从不/很少释放内存,只使用对象或记录引用。您可以拥有一个内存中的对象池,使用某种手动垃圾收集器,并且只处理对象(或记录)的引用。

如果对象中有string,则可能无法重新分配它们并维护已使用字符串的全局列表:引用计数和写入时复制将使其比标准序列化和分配快得多(即使使用FastMM4)。

在所有情况下,都值得对您的应用程序进行真正的分析。一般人类对性能瓶颈的猜测大多数时候都是错误的。只相信一个分析器和一个挂钟。也许你当前的实现并不是那么慢,真正的瓶颈不是对象进程,而是其他地方。

不要太早优化。 “在你加速之前做好准备。在你加快速度之前先说清楚。当你加快速度时保持正确。” - Kernighan和Plauger,编程风格的元素。

答案 2 :(得分:3)

克隆对象的一种简单方法是:

  • TPersistent
  • 中获取您的克隆类
  • 实施分配程序

请参阅此处的Memento设计模式示例: http://sourcemaking.com/design_patterns/memento/delphi/1

答案 3 :(得分:1)

我过去用于此情况的方法是使用我需要保留的对象部分声明记录,并在类中使用该记录。请参阅Optimizing Class Size in Delphi. Is there something like "packed classes"?上的旧答案。

当记录在赋值时被复制时,可以很容易地将字段从一种对象类型复制到另一种对象类型。

e.g。

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

答案 4 :(得分:0)

可以使用TMemoryStream实现撤消/重做 - 只需将对象的数据保存为流,并在出现重做/撤消时加载它。它使用对象的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);

我对我的CAD软件使用类似的方法,它的效果非常好。它还可以用于存储多个对象,在一个操作中进行更改。