Oop 对象Pascal:必须释放所有对象(类)吗?

Oop 对象Pascal:必须释放所有对象(类)吗?,oop,pascal,freepascal,delphi,Oop,Pascal,Freepascal,Delphi,我可以在不释放它们的情况下到处乱扔类es吗,或者我的软件会开始泄漏吗 例如,我可以这样做吗 Engine := TEngine.Create(TV); 然后毫无问题地摆脱引用,或者我必须首先调用它的Free方法吗 或者使用一个函数返回一个t某物,并且以后不必释放它的引用?一般规则是,如果创建它,就应该释放它。最好的方法是尝试。最后,如果您使用代码创建它: var Engine: TEngine; begin Engine := TEngine.Create(TV); try

我可以在不释放它们的情况下到处乱扔
es吗,或者我的软件会开始泄漏吗

例如,我可以这样做吗

Engine := TEngine.Create(TV);
然后毫无问题地摆脱引用,或者我必须首先调用它的
Free
方法吗


或者使用一个函数返回一个
t某物
,并且以后不必释放它的引用?

一般规则是,如果创建它,就应该释放它。最好的方法是尝试。最后,如果您使用代码创建它:

var
  Engine: TEngine;
begin
  Engine := TEngine.Create(TV);
  try
    // Do stuff with Engine
  finally
    Engine.Free;
  end;
end;
例外情况是,如果您有一个接受所有者作为参数的对象(例如像
TEdit
这样的可视控件或
TComponent
的非可视后代)。如果分配所有者,则在释放所有者时将释放它。(如果您在没有所有者的情况下创建它,您仍然需要自己释放它。)

如果该类是另一个对象的成员(字段),则在包含对象的构造函数中创建它,并在其析构函数中释放它:

type
  TOuterClass = class(TObject)
  private
    FEngine: TEngine;
  public
    constructor Create;
    destructor Destroy; override;
  end;

implementation

constructor TOuterClass.Create;
begin
  inherited;
  FEngine := TEngine.Create(TV);
end;

destructor TOuterClass.Destroy;
begin
  FEngine.Free;
  inherited;
end;

一般的规则是,如果你创建了它,你应该释放它。最好的方法是尝试。最后,如果您使用代码创建它:

var
  Engine: TEngine;
begin
  Engine := TEngine.Create(TV);
  try
    // Do stuff with Engine
  finally
    Engine.Free;
  end;
end;
例外情况是,如果您有一个接受所有者作为参数的对象(例如像
TEdit
这样的可视控件或
TComponent
的非可视后代)。如果分配所有者,则在释放所有者时将释放它。(如果您在没有所有者的情况下创建它,您仍然需要自己释放它。)

如果该类是另一个对象的成员(字段),则在包含对象的构造函数中创建它,并在其析构函数中释放它:

type
  TOuterClass = class(TObject)
  private
    FEngine: TEngine;
  public
    constructor Create;
    destructor Destroy; override;
  end;

implementation

constructor TOuterClass.Create;
begin
  inherited;
  FEngine := TEngine.Create(TV);
end;

destructor TOuterClass.Destroy;
begin
  FEngine.Free;
  inherited;
end;

从技术上讲,是的,必须显式释放使用构造函数初始化的所有内容

但有一些简单的解决方法,如果使用得当,可以为您省去大部分麻烦:

1:使用TInterfacedObject:

  IMyStuff = interface(IUnknown)
    ['{9DF82155-2475-4403-8933-969DC4912AD7}']
    function Print:boolean;
    procedure DoStuff;
  end;

  TMyStuff = class(TInterfacedObject, IMyStuff)
    private
      function Print:boolean;
      procedure DoStuff;
   end;
像其他类一样实现TMyStuff。但在代码中使用该类时,请使用IMyStuff类型的变量,如下所示:

procedure MyIProcedure;
var myStuff: IMyStuff;
begin

   myStuff:=TMyStuff.create;
   myStuff.DoStuff;

end;
无需在“TMyStuff.create”调用中强制转换(在本例中,有时是…),因为变量类型为IMystuff,这是隐式的。无需释放IMyStuff(事实上您不能,尽管您可以调用IMyStuff:=nil。)因为它被声明为一种接口类型,所以自动垃圾收集是使用COM引用计数模型实现的-当您从TInterfacedObject继承时,Delphi会为您处理此问题

但是不要混合类类型变量:即TMyStuff和IMyStuff变量类型。这会导致一些令人讨厌的混乱和错误,让你抓狂。这就是我通常将TInterfacedObject的成员声明为PRIVATE的原因,就像我在这里所做的那样,通过引用TMyStuff使它们在中不可访问。但是,可以通过对IMyStuff的引用来访问它们:根据定义,所有接口成员都是公共的

2:除了Ken的出色回答之外,VCL的TObjectList和TObjectDictionary还为它们包含的所有对象引用提供了自动垃圾收集(详细信息请参见Delphi文档)——但它们本身必须被释放,然后其余的都被释放


只要在TInterfacedObject的析构函数中释放ObjectList或ObjectDictionary,就可以在TInterfacedObject中使用TObjectList或TObjectDictionary,不再担心垃圾收集。当referenceCount=0时,Delph的实现将自动调用该析构函数。

技术上是的,必须显式释放使用构造函数初始化的所有内容

但有一些简单的解决方法,如果使用得当,可以为您省去大部分麻烦:

1:使用TInterfacedObject:

  IMyStuff = interface(IUnknown)
    ['{9DF82155-2475-4403-8933-969DC4912AD7}']
    function Print:boolean;
    procedure DoStuff;
  end;

  TMyStuff = class(TInterfacedObject, IMyStuff)
    private
      function Print:boolean;
      procedure DoStuff;
   end;
像其他类一样实现TMyStuff。但在代码中使用该类时,请使用IMyStuff类型的变量,如下所示:

procedure MyIProcedure;
var myStuff: IMyStuff;
begin

   myStuff:=TMyStuff.create;
   myStuff.DoStuff;

end;
无需在“TMyStuff.create”调用中强制转换(在本例中,有时是…),因为变量类型为IMystuff,这是隐式的。无需释放IMyStuff(事实上您不能,尽管您可以调用IMyStuff:=nil。)因为它被声明为一种接口类型,所以自动垃圾收集是使用COM引用计数模型实现的-当您从TInterfacedObject继承时,Delphi会为您处理此问题

但是不要混合类类型变量:即TMyStuff和IMyStuff变量类型。这会导致一些令人讨厌的混乱和错误,让你抓狂。这就是我通常将TInterfacedObject的成员声明为PRIVATE的原因,就像我在这里所做的那样,通过引用TMyStuff使它们在中不可访问。但是,可以通过对IMyStuff的引用来访问它们:根据定义,所有接口成员都是公共的

2:除了Ken的出色回答之外,VCL的TObjectList和TObjectDictionary还为它们包含的所有对象引用提供了自动垃圾收集(详细信息请参见Delphi文档)——但它们本身必须被释放,然后其余的都被释放


只要在TInterfacedObject的析构函数中释放ObjectList或ObjectDictionary,就可以在TInterfacedObject中使用TObjectList或TObjectDictionary,不再担心垃圾收集。当referenceCount=0时,Delph的实现将自动调用该析构函数。

如果TComponent所有者机制纯粹是一种库约定,那就再强调也不过分了。在语言级别上,它仍然必须被释放,只是FCL/VCL为您做了一些管理。@KenWhite您是否还必须释放引发的
Exception
s?@SuperDisk:No.:-)您没有创建相同的代码;你在抚养他们。“异常处理机制将为您解决这些问题。@KenWhite好的,最后一个问题。是否必须立即释放传递给过程的新类?例如,如果调用
DoThing(TCoord.Create(0,0))