它应该释放包含在其中的对象吗?

3

最近我遇到了一个困境。

考虑以下示例:


unit Unit2;

interface

uses
  Classes;

type
  TMyObject = class(TObject)
  private
    FDataStream: TMemoryStream;
    procedure SetDataStream(const Value: TMemoryStream);
  public
    property DataStream: TMemoryStream read FDataStream write SetDataStream;
    constructor Create(ADataStream: TMemoryStream);
    destructor Destroy;
  end;

implementation

{ TMyObject }

constructor TMyObject.Create(ADataStream: TMemoryStream);
begin
  FDataStream := ADataStream;
end;

destructor TMyObject.Destroy;
begin
  //Should MyObject free FDataStream?
end;

procedure TMyObject.SetDataStream(const Value: TMemoryStream);
begin
  FDataStream := Value;
end;

end.

正如您所见,TMyObject可以拥有TMemoryStream的实例。现在,我想知道当TMyobject被释放时,它应该怎么做?它也应该释放FDataStram吗?还是应该保持不变?

对于这种情况是否有任何指导方针呢?

谢谢。

4个回答

10

在你提供的例子中,我认为你的TMyObject从其他地方接收了一个实例。如果它在TMyObject构造函数中没有接管它所接收的流的所有权,那么当它被销毁时,它肯定不应该释放它。

为了保持理智,请坚持这个规则:实例化对象的类/代码应该销毁它

由于你的构造函数没有创建数据流实例,因此析构函数不应该释放它。


虽然总的来说,我同意粗体字中的观点,但你和我都知道有些情况并非如此(例如具有OwnsObjects语义的容器)。在这种情况下,我也不会释放流。 - Rudy Velthuis
@Rudy:当然可以。我会将其放在“转移所有权”标题下。可以作为传递给构造函数的“合同”的一部分,或者作为传递给容器/集合的Add方法的一部分。所有权转移是一种情况,你有意识地并且完全知道后果,从而违反了一个本来“坚不可摧”的规则。 - Marjan Venema

3

TMyObject没有自己创建TMemoryStream。通过构造函数,流被作为引用传递给了TMyObject,因此TMyObject不应该释放它。释放内存流的责任应由创建者承担。


1
如果使用以下方式创建对象:TMyObject.Create(TMemoryStream.Create);,那该怎么办呢?我可能会这样做。 - Rudy Velthuis
1
@Rudy,那么你会让TMyObject拥有并创建它吗? - johnny

1

你是否需要释放它取决于语义。如果你是流的唯一用户,并且(之前的)所有者认为你拥有它,那么在完成后应该释放它。

这可以通过用户代码来指示:

x := TMyCode.Create(TMemoryStream.Create));

当你是所有者时,用户只需创建流(内存、文件等)并将其交给你。

然而,如果你只是被其他代码拥有的内存流所授予,以便读取一部分,然后流的所有者将其传递给另一个类/例程,则它不属于你,你应该保持它的活性。

这完全取决于清晰的文档说明谁将释放什么。这并不像之前的帖子所描述的那样清晰。你(和你的类的用户)只能决定什么是最好的。

当然,你可以添加另一个布尔属性/参数,指示你的类是否拥有该对象。但只有在不清楚类是否应该这样做时才这样做。


问题在于如何检测流是否是按照您所展示的方式创建的。当然,我可以引入一个像OwnsStream这样的属性,但是当我必须通过构造函数传递更多对象时,这会变得混乱。最糟糕的部分是,如果该类对其用户是私有的,例如用户只有DCU,则可能会创建内存流并认为这是MyObject负责释放它... - Wodzu

1

拥有该对象的对象应该释放它。通常创建它的对象是所有者。在99%或更多的情况下都是这种情况。有时所有权会转移到另一个对象,当发生这种情况时,新所有者有责任释放该对象。

虽然我要指出,在您的示例中,无论您在Destroy中放置什么,它都不重要,因为它永远不会被调用-您忘记使用override指令标记它。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接