首页 文章

Delphi XE7智能指针

提问于
浏览
4

我是具有C背景的Delphi的新手,并试图弄清楚如何实现智能指针 . 我遇到了以下帖子,我试图将其作为我自己的起点:Delphi - smart pointers and generics TList

但是我无法使用Delphi XE7编译以前的代码(编译器错误在代码中显示为注释) . 如果有人真正解释了代码的逻辑,我也会非常感激(最初我想将该类用作实用程序类中的一个drop,但现在我想了解实际发生的情况) . 我隐约明白,因为智能指针实现继承自TInterfacedObject,所以它是引用计数但超出此范围的任何东西对我来说都没有意义:)

unit SmartPointer;

interface

uses
  SysUtils, System.Generics.Collections;

type
  ISmartPointer<T> = reference to function: T;

  // complains ISmartPointer<T> expecting an interface type
  TSmartPointer<T: class, constructor> = class(TInterfacedObject,ISmartPointer<T>)
  private
    FValue: T;
  public
    constructor Create; overload;
    constructor Create(AValue: T); overload;
    destructor Destroy; override;
    function Invoke: T;
  end;

implementation

{ TSmartPointer<T> }

constructor TSmartPointer<T>.Create;
begin
  inherited;
  FValue := T.Create;
end;

// complains: overload procedure TSmartPointer.Create must be marked with the overload directive
constructor TSmartPointer<T>.Create(AValue: T);
begin
  inherited Create;
  if AValue = nil then
    FValue := T.Create
  else
    FValue := AValue;
end;

destructor TSmartPointer<T>.Destroy;
begin
  FValue.Free;
  inherited;
end;

function TSmartPointer<T>.Invoke: T;
begin
  Result := FValue;
end;

end.

试图使用以前的智能指针与以下测试代码导致编译器错误...我错过了什么?

program TestSmartPointer;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils, SmartPointer;

type
TPerson = class
  private
    _name : string;
    _age : integer;
  public

    property Name: string read _name write _name;
    property Age: integer read _age write _age;
  end;

var
  pperson : TSmartPointer<TPerson>;

begin
  try
    { TODO -oUser -cConsole Main : Insert code here }
    pperson := TSmartPointer<TPerson>.Create();
    // error on next line: undeclared Identifier: Name
    pperson.Name := 'John Doe';
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

1 回答

  • 6

    您必须将引用变量声明为 ISmartPointer<TPerson>

    var
      pperson : ISmartPointer<TPerson>;
    

    下面的代码也会编译,但在这种情况下它不会自动释放内存,因为当您将引用计数的对象实例存储到对象引用中时,您会搞乱其引用计数机制 . 根据代码,它可能导致内存泄漏或底层对象实例的过早破坏 .

    var
      pperson : TSmartPointer<TPerson>;
    
    begin
      pperson := TSmartPointer<TPerson>.Create();
      pperson.Invoke.Name := 'John Doe';
    

    最后,以下代码说明了正确的智能指针用法:

    var
      pperson : ISmartPointer<TPerson>;   // note pperson is ISmartPointer<TPerson>
    
    begin
      pperson := TSmartPointer<TPerson>.Create();
      pperson.Name := 'John Doe';
    

    Some interface basics

    接口定义了一个 Contract - 实现接口的类必须具有的功能,而不提供特定的实现 . IFoo 接口声明意味着当您引用 IFoo 时,可以在该引用上调用 Foo 方法,但这就是您所能做到的 .

    IFoo = interface
      procedure Foo;
    end;
    

    当类实现接口时,它必须实现该接口的所有方法 . IFoo 中的方法 Foo 将从 TFooTOtherFoo 映射到方法 Foo . 特定接口的实现可以在不同的类中不同 .

    TFoo = class(TInterfacedObject, IFoo)
    public
      procedure Foo;
      procedure Bar;
    end;
    
    TOtherFoo = class(TInterfacedObject, IFoo)
    public
      procedure Foo;
    end;
    
    procedure TFoo.Bar;
    begin
      writeln('Bar');
    end;
    
    procedure TFoo.Foo;
    begin
      writeln('Foo');
    end;
    
    procedure TOtherFoo.Foo;
    begin
      writeln('Other Foo');
    end;
    
    var
      foo: IFoo;
      f: TFoo;
    
      foo := TFoo.Create;
      foo.Foo; // Output -> Foo
    
      // Compiler error -> foo is interface reference and only knows Foo from TFoo
      foo.Bar;
    
      foo := TOtherFoo.Create;
      foo.Foo; // Output -> Other Foo
    
      // Mixing object reference with reference counted object instance -> memory leaks
      f := TFoo.Create;
      foo.Foo; // output -> Foo
      foo.Bar; // f is TFoo object reference, and it knows everything from TFoo
    

    How a smart pointer actually works

    ISmartPointer<T> 被声明为匿名函数 .

    ISmartPointer<T> = reference to function: T;
    

    上面的声明等同于 Invoke 函数的接口

    ISmartPointer<T> = interface
      function Invoke: T;
    end;
    

    两者之间的区别(我们感兴趣的是),使用匿名函数/方法,您不必显式调用 Invoke ;编译器会为你做这件事 .

    由于 ISmartPointer<T> 是一个匿名函数,它实际上是 TSmartPointer<T> 类声明中的接口, Invoke 方法将映射到 ISmartPointer<T> .

    TSmartPointer<T: class, constructor> = class(TInterfacedObject, ISmartPointer<T>)
      private
        FValue: T;
      public
        constructor Create; overload;
        constructor Create(AValue: T); overload;
        destructor Destroy; override;
        function Invoke: T;
      end;
    
    var
      pperson : ISmartPointer<TPerson>;
    

    因此,当您在窗帘后写 pperson.Name 转换为 pperson.Invoke 函数调用,该函数从 FValue 返回 TPerson 实例, TPerson 具有编译器可识别的 Name 属性 .

    由于 TSmartPointer<T> 是引用计数类,因此当您使用 ISmartPointer<T> 引用时,基础 TSmartPointer<T> 对象实例以及 FValue 中包含的 T 实例将在 ISmartPointer<T> 引用超出范围时自动释放,或者将其设置为 nil in码 .

相关问题