复制Delphi对象的正确方法
使用构造函数或实例函数复制对象实例的优点和缺点是什么?
示例 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);
What are pros and cons of duplication an object instance with constructor or instance function?
Example 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;
Example 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;
One major difference immediately springs to mind - in the latter case the Create constructor would have to be virtual so that a class hierarchy supporting Clone could be built basing on the TMyObject.
Assume that this is not a problem - that TMyObject and everything based on it is entirely under my control. What is your preferred way of doing copy constructor in Delphi? Which version do you find more readable? When would you use former or latter approach? Discuss. :)
EDIT:
My main concern with the first example is that the usage is very heavy compared to the second approach, i.e.
newObj := TMyObject.Create(oldObj)
vs.
newObj := oldObj.Clone;
EDIT2 or "Why I want single-line operation"
I agree that Assign is a reasonable approach in most cases. It's even reasonable to implement 'copy constructor' internally by simply using assign.
I'm usually creating such copies when multithreading and passing objects through the message queue. If object creation is fast, I usually pass a copy of the original object because that really simplifies the issues of object ownership.
IOW, I prefer to write
Send(TMyObject.Create(obj));
or
Send(obj.Clone);
to
newObj := TMyObject.Create;
newObj.Assign(obj);
Send(newObj);
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
第一个添加有关要创建哪个对象的信息,第二个则不添加。这可用于实例化类的后代或祖先。Delphi
方式 (
TPercient
) 将创建和克隆分开:并且具有与您显式选择要实例化的类相同的属性。但您不需要两个构造函数,一个用于正常使用,另一个用于您想要克隆的地方。
由于单行要求进行编辑
当然,您可以使用 Delphi 元类(未经测试)混合它。
其余的,只需实现您的
assign()
运算符,并且您可以混合多种方式。edit2
我用 D2009 中测试的代码替换了上面的代码。类型的一些依赖关系可能会让您感到困惑,希望这样可以更清楚。当然,你必须研究分配机制。我还测试了
metaclass=nil
默认参数并且它有效,所以我添加了它。Ps (2024) 很高兴看到类助手是否也可以用于非根类实现这一点。
The first adds information about which object to want to create, the second not. This can be used to instantiate e.g. a descendant or an ancestor of a class
The Delphi way (
TPersistent
) separates creation and cloning:and has this same property that you explicitly choose the class to instantiate. But you don't need two constructors, one for normal use, and one where you want to clone.
edit due to oneline requirement
You can mix it of course using Delphi metaclasses (untested)
For the rest, just implement your
assign()
operators, and you can mix multiple ways.edit2
I replaced the code above with code tested in D2009. There are some dependencies of the types that might have confused you, hope it is clearer this way. Of course you'll have to study the assign mechanism. I also tested the
metaclass=nil
default parameter and it works, so I added it.P.s. (2024) it would be nice to see if class helpers can be used to implement this also for non root classes.
我认为没有正确的方法,这取决于个人风格。 (正如 Marco 指出的,还有更多的方法。)
如果您使用流实现分配,那么您只需担心哪些字段需要可用。
I don't think there is a correct way it just depend on personal style. (And as Marco pointed out, there are more ways.)
And if you implement Assign using streams, you have only one place to worry about which fields need to be available.
我喜欢克隆风格——但仅限于Java(或任何其他GC语言)。我在 Delphi 中使用过它几次,但大多数时候我还是使用 Create 和 Assign ,因为谁负责对象的销毁更加清晰。
I like the clone style - but only in Java (or any other GC language). I used it some times in Delphi, but mostly I stay with
Create
andAssign
, because it is much clearer who is responsible for the destruction of the object.我使用第二种方法,即带有 Clone 函数的方法,即使对于复杂的类,它也很有魅力。我发现它更具可读性和防错性。
I use the second method, the one with the Clone function, and it works like a charm, even with complex classes. I find it more readable and error proof.