复制Delphi对象的正确方法

问题描述 投票:0回答:4

使用构造函数或实例函数复制对象实例有什么优缺点?

示例A:

type
  TMyObject = class
  strict private
    FField: integer; 
  public
    constructor Create(srcObj: TMyObject); overload; 
    //alternatively:
    //constructor CreateFrom(srcObj: TMyObject);
    property Field: integer read FField;
  end;

constructor TMyObject.Create(srcObj: TMyObject);
begin
  inherited Create;
  FField := srcObj.Field;
end;

示例 B:

type
  TMyObject = class
  strict private
    FField: integer; 
  public
    function Clone: TMyObject;
    property Field: integer read FField;
  end;

function TMyObject.Clone: TMyObject;
begin
  Result := TMyObject.Create;
  Result.FField := FField;
end;

我立即想到了一个主要区别 - 在后一种情况下,Create 构造函数必须是虚拟的,以便可以基于 TMyObject 构建支持 Clone 的类层次结构。

假设这不是问题——TMyObject 和基于它的一切都完全在我的控制之下。在 Delphi 中执行复制构造函数的首选方法是什么?您觉得哪个版本更具可读性?您什么时候会使用前一种方法或后一种方法?讨论。 :)

编辑: 我对第一个例子主要担心的是,与第二种方法相比,使用量非常大,即

newObj := TMyObject.Create(oldObj)

对比

newObj := oldObj.Clone;

EDIT2 或“为什么我想要单行操作”

我同意在大多数情况下分配是一种合理的方法。通过简单地使用赋值在内部实现“复制构造函数”甚至是合理的。

我通常在多线程处理并通过消息队列传递对象时创建此类副本。如果对象创建速度很快,我通常会传递原始对象的副本,因为这确实简化了对象所有权问题。

IOW,我更喜欢写

Send(TMyObject.Create(obj));

Send(obj.Clone);

newObj := TMyObject.Create;
newObj.Assign(obj);
Send(newObj);
delphi constructor copy-constructor
4个回答
33
投票

第一个添加有关要创建哪个对象的信息,第二个则不添加。这可以用来实例化例如一个阶级的后代或祖先

Delphi 方式 (

TPersistent
) 将创建和克隆分开:

dest := TSomeClass.Create; 
dest.Assign(source);  

并且具有与您显式选择要实例化的类相同的属性。但你不需要两个构造函数,一个用于正常使用,一个用于你想要克隆的地方。

由于单行要求进行编辑 当然,您可以使用 Delphi 元类(未经测试)混合它

type
  TBaseSomeObject = class;
  TBaseObjectClass = class of TBaseSomeObject;

  TBaseSomeObject = class(TPersistent)
    function Clone(t: TBaseObjectClass = nil): TBaseSomeObject; virtual;
  end;

...

  function TBaseSomeObject.Clone(t: TBaseObjectClass = nil): TBaseSomeObject;
  begin
    if Assigned(t) then
      Result := t.Create
    else
      Result := TBaseObjectClass(Self.ClassType).Create;
    Result.Assign(Self);
  end;


 SendObject(obj.Clone); // full clone.
 SendObject(obj.Clone(TDescandantObject)); // Cloned into Descendant object 

对于其余的,只需实现您的

assign()
运算符,您可以混合多种方式。

编辑2

我用D2009中测试的代码替换了上面的代码。类型的一些依赖关系可能会让您感到困惑,希望这样可以更清楚。当然,你必须研究分配机制。我还测试了

metaclass=nil
默认参数并且它有效,所以我添加了它。

P.s. (2024) 很高兴看到类助手是否也可以用于非根类实现这一点。


7
投票

我认为没有正确的方法,这取决于个人风格。 (正如 Marco 指出的,还有更多方法。)

  • 构造函数的方式很短,但是它违反了构造函数必须只构造对象的原则。这可能不是问题。
  • 尽管需要为每个类提供调用,但克隆方式很短。
  • 分配方式更像Delphi。它将创建和初始化分开,这很好,因为我们喜欢“一种方法一种函数”的概念,这使得代码更易于维护。

如果您使用流实现分配,您只需担心哪些字段需要可用。


4
投票

我喜欢 clone 风格 - 但仅限于 Java(或任何其他 GC 语言)。我在 Delphi 中使用过几次,但大多数情况下我还是使用

Create
Assign
,因为谁负责对象的销毁更清楚。


0
投票

我使用第二种方法,即带有 Clone 函数的方法,即使对于复杂的类,它也能发挥作用。我发现它更具可读性和防错性。

© www.soinside.com 2019 - 2024. All rights reserved.