Delphi 我可以通过复制对象的内存来克隆对象吗?
我需要对我控制的有限数量的类使用undo+redo堆栈,这些类必须非常快,使用RTTI和XML或streams是不可行的,因为嵌套对象列表中的实例数可能高达2000多个。需要通过memento模式将对象复制进和复制出,并立即重新加载Delphi 我可以通过复制对象的内存来克隆对象吗?,delphi,design-patterns,delphi-xe,Delphi,Design Patterns,Delphi Xe,我需要对我控制的有限数量的类使用undo+redo堆栈,这些类必须非常快,使用RTTI和XML或streams是不可行的,因为嵌套对象列表中的实例数可能高达2000多个。需要通过memento模式将对象复制进和复制出,并立即重新加载 有没有办法通过复制内存并从该内存中重新实例化对象来克隆对象 几乎没有。您可以轻松地复制对象的内存,但该内存的一部分将是指针,在这种情况下,您只复制引用。这些指针还可以包括字符串和其他对象 我认为最好的方法是从TPersistent(或任何Decentant)继承这些
有没有办法通过复制内存并从该内存中重新实例化对象来克隆对象 几乎没有。您可以轻松地复制对象的内存,但该内存的一部分将是指针,在这种情况下,您只复制引用。这些指针还可以包括字符串和其他对象
我认为最好的方法是从TPersistent(或任何Decentant)继承这些类,然后为每个类实现Assign方法。这样,就可以创建第二个实例并将对象指定给该新实例。在Assign实现中,您可以自行决定复制哪些数据,不复制哪些数据。克隆对象的简单方法是:
- 从TPersistent派生可克隆类
- 执行分配程序
可以使用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软件使用了类似的方法,它工作得相当好。它还可用于存储在一次操作中更改的多个对象 2000+嵌套对象没有那么大,也不会那么慢,即使使用RTTI(磁盘访问或压缩会慢得多)。使用直接SaveToStream手动序列化,如果使用FastMM4(自Delphi 2006以来的默认内存管理器),速度非常快 也许您可以更改算法并改用动态数组(有一个开源序列化程序)。但您的数据可能不符合此类记录 或者从不/很少释放内存,只使用对象或记录引用。您可以有一个内存中的对象池,带有某种手动垃圾收集器,并且只处理对对象(或记录)的引用 如果对象中有
string
,则可能无法重新分配它们并维护已用字符串的全局列表:引用计数和写入时复制将使其比标准序列化和分配快得多(即使使用FastMM4)
在所有情况下,对应用程序进行真正的分析都是值得的。人们对性能瓶颈的一般猜测大多是错误的。只信任一个剖析器和一个挂钟。也许您当前的实现没有那么慢,真正的瓶颈不是对象进程,而是其他地方
不要过早优化。“在加速之前要正确。在加速之前要清楚。在加速时要正确。”-Kernighan和Plauger,《编程风格的要素》。我过去在这种情况下使用的一种方法是声明一条记录,其中包含我需要保留的对象部分,并在类中使用该记录。有关示例,请参阅我的旧答案 由于记录是在赋值时复制的,因此很容易将字段从一种对象类型复制到另一种对象类型 e、 g
当然,当您使用XE时,您可以使用它。那么,如果不克隆对象,您就不能实现memento模式吗?您的所有对象都是从TPersistent继承的,因此支持TPersistent。分配是您的最佳选择吗?只要对象不包含引用,您就可以这样做。问题是,对象超出了它的内存。关于打开的文件、其他句柄、同步原语、与refcounting的接口等呢。?只有对象本身知道如何正确地复制自己。您的分析实验表明您需要多大的速度?您的评测参考实现是什么?Assign是一个候选者,可能是最安全的。ToObject没有SaveToStream。我不是说ToObject,我知道它没有持久性函数。那么您是什么意思?请编辑您的答案以更准确。你在句子的中间大写对象,表示你的意思是代码> ToStudio< /Cord>。如果你不是想让它成为一个专有名词,你还需要在它前面放一个冠词(例如,an或the)。对不起,罗布,我主要说德语和写德语,在这种语言中,名词必须大写。是我的错。我会更正我的答案。谢谢你的洞察力。使用克隆和分配方法确实直接依赖于内存管理器,正如您所指出的那样,内存管理器的速度非常快-关键部分是关注内存管理器,并通过有选择地填充克隆并在内部将克隆标记为克隆来智能地利用它,避免RTTI。@MX4399还值得尝试全局
string
list:使用引用计数肯定比流方法快。当然,Assign()。
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;