【问题标题】:Does class(TInterfacedObject) need a destructor in Delphi?在 Delphi 中 class(TInterfacedObject) 是否需要析构函数?
【发布时间】:2019-12-02 14:14:23
【问题描述】:

我在从未调用过Destroy() 的情况下运行。

unit Unit2;

interface

type

// Interface
ITest = Interface(IInterface)
 function IsTrue() : Boolean;
end;

TmyClass = class(TInterfacedObject, ITest)
  public
    // Interface implementation
    function IsTrue() : Boolean;

    constructor Create();
    destructor Destroy(); override;
end;

implementation

constructor TmyClass.Create();
begin
  inherited Create();
end;

destructor TmyClass.Destroy();
begin
  inherited Destroy();
end;

published
  // Property
  property IsItTrue: Boolean read IsTrue;
end.

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, 
  System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, 
  Vcl.Dialogs, Vcl.StdCtrls, unit2;

type
  TForm1 = class(TForm)
  Button1: TButton;
  procedure FormCreate(Sender: TObject);
  procedure Button1Click(Sender: TObject);
private
  { Private declarations }
public
  { Public declarations }
end;

var
  Form1: TForm1;
  fMyClass: TmyClass;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
  fMyClass.Free;  // if refcount = 0 this works, if refcount <> 0 pointer error.
  //or
  fMyClass := Nil; // no error but Destroy wil not be executed
  Close();
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  fMyClass := TMyClass.Create();
end;
end.

阅读this article,只有构造函数,没有实现析构函数。

这有什么特别的原因吗?

我是否应该通过实现finalization 部分来释放(如果需要)将在myClass 中定义的所有其他对象?

【问题讨论】:

  • 文章中的类没有任何需要清理的资源,所以不需要重写析构函数。你应该找出为什么你的析构函数没有被调用。
  • 您的问题是无法回答的。没有minimal reproducible example,所以我们不知道为什么你的析构函数没有被调用。是否需要覆盖默认析构函数是另一个问题,这取决于您在类中声明的字段类型以及您如何使用它们。
  • 这里似乎有两个单独的问题:1) TInterfacedObject 是否需要重写其析构函数,以及 2) 为什么永远不会调用您的析构函数。但经验法则是肯定的,它在很大程度上与其他对象一样工作,所以如果你在构造函数中创建/初始化任何东西,或者一般需要任何清理,那么你需要在析构函数中销毁它。为什么从不调用析构函数,如果没有看到足够的代码很难说。
  • TInterfacedObject 被引用计数。你的类的声明很好(假设你只处理过IInterface变量),但是如果你从来没有将你的类的对象实例分配给接口变量,那么引用计数将不会被正确管理,所以析构函数不会不叫。请举例说明您如何实际使用该课程,有人可以解释为什么它不能正常工作。
  • @Remy 原来的类和 api 有很多代码要在这里发布。所以,我想简化这个问题。虽然我必须说接口实际上是一个 IMFSourceReaderCallback。但是这个接口的实现方式与我的简单示例中的方式相同。我没有摆脱在 Create() 上初始化的引用计数,它保持为 2。

标签: delphi destroy tinterfacedobject


【解决方案1】:

没有调用析构函数的最可能原因是您没有将对象分配给接口变量。

procedure Test1;
var
  vMyObj : TObject;
begin
  vMyObj := myclass.Create;
end; <-Destructor NOT called here

procedure Test2;
var
  vMyIntf : IInterface;
begin
  vMyIntf := myclass.Create;
end; <-Destructor IS called here.

如果是这样,我邀请您阅读this answer 了解更多信息。

【讨论】:

  • 如果这个猜测是正确的,那么这个问题就是骗人的
  • 声明是 'var fMyClass: myClass;'它的创建方式如下:'fmyClass := MyClass.Create();' Embarcadero 说:“使用 TInterfacedObject 作为基类,因为它实现了 IInterface 的方法。”那么,这就是我所做的吗?
  • @ToKa 接口变量和对象变量是两个不同的东西。变量是声明为 TObject 还是声明为 myclass 并不会改变它仍然是对象变量而不是接口变量的事实。如果您将声明更改为var fMyClass : IInterface,您将看到当变量设置为 nil(隐式或显式)时,您的析构函数被正确调用
  • @Ken 我认为我们在这里有一个误解。例如:如果我要在类的公共字段中添加一个字段,例如 'sMyFileName: string;',并以类似 'fMyClass.sMyFileName := 'True.txt'' 的调用形式,编译器会报告:“ fMyClass.sMyFileName 不包含成员等。”。原始源代码的问题,这里太大了,将引用计数保持在 2。在初始化 Create() 时直接初始化引用计数。所有其他初始化的对象都在计数,但可以在销毁之前释放。 >>
  • >> 但是.. 初始引用计数保持为 2,这将在销毁时产生指针错误。我不知道为什么。
【解决方案2】:

您的fMyClass 变量是对象引用,而不是接口,因此它不参与TInterfaceObject 的引用计数。

你需要改变这个:

fMyClass: TmyClass;

到这里:

fMyClass: ITest;

然后你可以完全摆脱fMyClass.Free;

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, 
  System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, 
  Vcl.Dialogs, Vcl.StdCtrls, unit2;

type
  TForm1 = class(TForm)
  Button1: TButton;
  procedure FormCreate(Sender: TObject);
  procedure Button1Click(Sender: TObject);
private
  { Private declarations }
public
  { Public declarations }
end;

var
  Form1: TForm1;
  fMyClass: ITest;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
  fMyClass := nil; // no error and Destroy will be executed
  Close();
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  fMyClass := TMyClass.Create();
end;

end.

fMyClass := nil; 仅当fMyClass 是接口变量而不是对象引用并且您不能在接口变量上调用Free() 时才会调用引用计数。

【讨论】:

  • 谢谢雷米。我完全错过了。
猜你喜欢
  • 1970-01-01
  • 2018-06-19
  • 1970-01-01
  • 2011-10-11
  • 2013-05-22
  • 1970-01-01
  • 2021-05-30
  • 1970-01-01
  • 2016-12-02
相关资源
最近更新 更多