首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Delphi XE7智能指针

Delphi XE7智能指针
EN

Stack Overflow用户
提问于 2015-03-31 20:43:12
回答 1查看 2.9K关注 0票数 6

我是德尔福的新手,有着C++背景,我想弄清楚智能指针是如何实现的。我遇到了下面的帖子,我正在尝试使用它作为我自己的起点:Delphi - smart pointers and generics TList

但是,我无法使用Delphi编译前面的代码(编译器错误在代码中显示为注释)。此外,如果有人真的解释了代码的逻辑(最初我想使用这个类作为实用程序类中的一滴,但现在我想了解实际发生了什么),我会非常感激的。我模糊地理解到,由于智能指针实现是从TInterfacedObject继承的,所以引用是计数的,但除此之外的任何内容对我来说都是毫无意义的:)

代码语言:javascript
复制
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.

尝试使用前面的智能指针和下面的测试代码,这会导致编译器错误…我遗漏了什么?

代码语言:javascript
复制
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.
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2015-04-01 18:12:22

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

代码语言:javascript
复制
var
  pperson : ISmartPointer<TPerson>;

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

代码语言:javascript
复制
var
  pperson : TSmartPointer<TPerson>;

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

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

代码语言:javascript
复制
var
  pperson : ISmartPointer<TPerson>;   // note pperson is ISmartPointer<TPerson>

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

一些接口基础

接口定义了实现接口的类必须具有的契约功能,而不提供特定的实现。IFoo接口声明意味着当您对IFoo进行引用时,可以在该引用上调用Foo方法,但这是您所能做到的全部。

代码语言:javascript
复制
IFoo = interface
  procedure Foo;
end;

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

代码语言:javascript
复制
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

智能指针的实际工作方式

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

代码语言:javascript
复制
ISmartPointer<T> = reference to function: T;

上述声明相当于与Invoke函数的接口。

代码语言:javascript
复制
ISmartPointer<T> = interface
  function Invoke: T;
end;

这两种方法的不同之处(我们在这里感兴趣的一种)是,对于匿名函数/方法,您不必显式调用Invoke;编译器将为您完成这一任务。

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

代码语言:javascript
复制
  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

票数 7
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/29378203

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档