【问题标题】:Delphi interface reference count mechanismDelphi 接口引用计数机制
【发布时间】:2017-07-18 20:58:18
【问题描述】:

确实,网上有很多关于这个的东西,但我读的越多,我就越困惑。我编写了一个名为Combinatorics 的组件,它可以处理一些数学概率问题。代码非常简短易懂,因为我不想让它变得复杂。我在这里做一个小预览:

//Combinatorio.pas
type
 ICombinatorio = interface
  function getSoluzioni(): integer; //soluzioni means "Solutions"
  function getFormula(): string;
 end;

//ImplCombinatorio.pas
type

 TCombinazioni = class(TInterfacedObject, ICombinatorio)
  private
   n, k: integer;
   ripetizione: boolean;
   function fattoriale(const x: integer): integer;
  public
   constructor Create(const n, k: integer; const ripetizione: boolean);
   function getSoluzioni(): integer;
   function getFormula(): string;
 end;

 TDisposizioni = class(TInterfacedObject, ICombinatorio)
  private
   n, k: integer;
   ripetizione: boolean;
   function fattoriale(const x: integer): integer;
  public
   constructor Create(const n, k: integer; const ripetizione: boolean);
   function getSoluzioni(): integer;
   function getFormula(): string;
 end;

 TPermutazioni = class(TInterfacedObject, ICombinatorio)
  private
   n: integer;
   k: string;
   ripetizione: boolean;
   function fattoriale(const x: integer): integer;
  public
   constructor Create(const n: integer; const k: string; ripetizione: boolean);
   function getSoluzioni(): integer;
   function getFormula(): string;
 end;

你不需要看函数和过程是如何实现的,这对问题并不重要(你可以很容易地想象它们做了什么)。


这是我的第一个组件,我已经编译并安装了它,它可以工作。但是我有些看不懂。

unit TCombinatorio;

interface

uses
  System.SysUtils, System.Classes, Combinatorio, ImplCombinatorio;

type
 cCombinatorio = (cNull = 0, cDisposition = 1, cPermutation = 2, cCombination = 3);

type
 TCombinatorics = class(TComponent)
 strict private
  { Private declarations }
  Fn, Fk: integer;
  FRep: boolean;
  FType: cCombinatorio;
  FEngine: ICombinatorio;
  procedure Update;
 public
  { Public declarations }
  constructor Create(AOwner: TComponent); override;
  function getSolution: integer;
  function getFormula: string;
 published
  property n: integer read Fn write Fn;
  property k: integer read Fk write Fk;
  property kind: cCombinatorio read FType write FType default cNull;
  property repetitions: boolean read FRep write FRep;
end;

procedure Register;

implementation

procedure Register;
begin
 RegisterComponents('RaffaeleComponents', [TCombinatorics]);
end;

{ TCombinatorics }

constructor TCombinatorics.Create(AOwner: TComponent);
begin

 inherited Create(AOwner);
 Fn := 0;
 Fk := 0;
 FType := cNull;
 repetitions := false;

end;

function TCombinatorics.getFormula: string;
begin
 Update;
 Result := FEngine.getFormula;
end;

function TCombinatorics.getSolution: integer;
begin
 Update;
 Result := FEngine.getSoluzioni;
end;

procedure TCombinatorics.Update;
begin

 case FType of
  cDisposition:
   FEngine := TDisposizioni.Create(n, k, repetitions);
  cPermutation:
   FEngine := TPermutazioni.Create(n, '', repetitions);
  cCombination:
   FEngine := TCombinazioni.Create(n, k, repetitions);
  cNull:
   raise Exception.Create('You have to select a type.');
 end;

end;

end.

查看Update; 过程。我之所以创建它是因为当用户以表单形式删除组件 (link) 时,他必须在对象检查器中(或在某处使用代码)设置构造函数中所需的 3 个重要参数。

由于FEngine: ICombinatorio 我可以为它分配一个类(TCombinazioni、TDisposizioni 或 TPermutazioni)而无需最后尝试,因为存在引用计数机制。我不确定我是否正确编码。假设:

  1. 用户选择cDisposition并进行计算
  2. 用户选择cDisposition(不同的值)并进行计算
  3. 用户选择cPermutation并进行计算

我一直在处理FEngine。参考计数如何变为零?当表单(和组件)破坏时它会归零吗?我希望我能很好地解释我不明白的地方。 FEngine 是一个私有变量,我在运行时分配给它不同的类(调用 Create)。当表单销毁或分配新类时,引用计数是否变为 0?

我像上面那样编码,因为 nick hodges 在他的书中做到了这一点,我当然相信他,但我想知道我是做什么的。

【问题讨论】:

  • 您可以简单地将析构函数添加到您的接口对象并放置断点以找出发生了什么。
  • 我猜你指的是“delphi中编码”的TEncryption示例;)我刚刚检查了这本书,当他谈到时,你应该在章节中间找到你的问题的答案TInterfacedObject!
  • @Sertac 我不知道你在说什么对不起,我现在正在学习,所以我觉得问起来更容易
  • @Alberto 是那本书的第 2 章 :) 是的,他解释得很好,但我不太确定我从这一章中得到了什么
  • @Raffaele 好的。如果您想了解更多信息,您可以立即尝试。将析构函数放入您的接口对象(TCombinazioni,...)。他们只需拨打inherited 即可。然后在“继承”语句上放置断点。然后在“编译”项目选项中选择“使用调试dcus”。并运行项目。当调试器停止时检查调用堆栈。

标签: delphi


【解决方案1】:

根据可以看到的代码,在第一次调用Update时,会创建一个新的ICombinatorio的实现者并赋值给FEngine;引用计数将为 1。在调用 Update 之后,将创建 ICombinatorio 实现者的另一个新实例(其引用计数将为 1)并分配给 FEngineFEngine 指向的前一个实现实例将减少其引用计数;如果为零,则将被销毁。 (它可能将基于您的代码示例)。

另外,当组件的析构函数被调用时(当拥有的 Form 被销毁时),隐式实例清理代码会将 FEngine 设置为 nil,这将减少引用计数(并且,根据您的示例, 将被销毁)。

因此,根据您的代码示例,我希望您的代码能够正常工作;干净地实例化和销毁ICombinatorio 接口对象。

【讨论】:

  • Aaaaah 好的,所以当我向 FEngine 分配一个新实例时,旧实例的 ref 为 0(因此它被销毁)而新实例变为 1。我认为引用没有为 0,但有一个总和。你说它是 0-1-0-1-0-1 ......但我认为它就像 0-1-2-3-4 然后最后是 0(在销毁时)。谢谢
  • 请参阅 system.pas 中的_IntfCopy 了解它是如何实现的,以及那里的评论以供推理。
  • @Raffaele:不,它不会去 0-1-0-1 等等......对接口的引用越多,引用计数就越高。但是在您的示例中,接口的引用计数(不是:变量,不要混淆)是1。如果分配了另一个实例,则该新实例的引用计数会增加,并且实例的引用计数是被替换是递减的。如果递减后 refcount 变为 0,它会自行终止。如果您有其他变量引用该实例,那么它将大于 1,递减不会使 refcount 为 0,它会保持活动状态。
猜你喜欢
  • 2014-05-04
  • 1970-01-01
  • 2021-08-17
  • 2012-02-23
  • 2014-04-07
  • 1970-01-01
  • 2018-03-04
  • 2010-09-15
  • 1970-01-01
相关资源
最近更新 更多