Delphi XE7智能指针
Delphi XE7 smart pointers
我是 Delphi 的新手,具有 C++ 背景,正在尝试弄清楚如何实现智能指针。我遇到了以下 post,我试图将其用作我自己的起点:
但是我无法使用 Delphi XE7 编译以前的代码(编译器错误显示为代码中的注释)。另外,如果有人真正解释了代码的逻辑,我将不胜感激(最初我想使用 class 作为实用程序 class 的一个下降,但现在我想了解实际发生的事情)。我隐约明白,因为智能指针实现是从 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.
您必须将引用变量声明为 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';
一些界面基础知识
一个接口定义了一个契约——实现该接口的 class 必须具备的功能,而不提供具体的实现。 IFoo
接口声明意味着当您引用 IFoo
时,您可以对该引用调用 Foo
方法,但仅此而已。
IFoo = interface
procedure Foo;
end;
当 class 实现接口时,它必须实现该接口的所有方法。 IFoo
中的方法 Foo
将映射到 TFoo
中的方法 Foo
或 TOtherFoo
。具体接口的实现在不同的classes.
可以不同
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>
声明为匿名函数。
ISmartPointer<T> = reference to function: T;
以上声明等同于带有 Invoke
函数的接口
ISmartPointer<T> = interface
function Invoke: T;
end;
两者之间的区别(我们在这里感兴趣的)是匿名 function/methods 你不必显式调用 Invoke
;编译器会为你做的。
由于ISmartPointer<T>
是一个匿名函数,在TSmartPointer<T>
的声明中实际上是一个接口class,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
函数调用时 returns 来自 FValue
和 [=] 的 TPerson
实例36=]有编译器可以识别的Name
属性。
由于 TSmartPointer<T>
是引用计数 class,当您使用 ISmartPointer<T>
引用时,基础 TSmartPointer<T>
对象实例以及 T
它包含在 FValue
中的实例将在 ISmartPointer<T>
引用超出范围时自动释放,或者您在代码中将其设置为 nil
。
我是 Delphi 的新手,具有 C++ 背景,正在尝试弄清楚如何实现智能指针。我遇到了以下 post,我试图将其用作我自己的起点:
但是我无法使用 Delphi XE7 编译以前的代码(编译器错误显示为代码中的注释)。另外,如果有人真正解释了代码的逻辑,我将不胜感激(最初我想使用 class 作为实用程序 class 的一个下降,但现在我想了解实际发生的事情)。我隐约明白,因为智能指针实现是从 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.
您必须将引用变量声明为 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';
一些界面基础知识
一个接口定义了一个契约——实现该接口的 class 必须具备的功能,而不提供具体的实现。 IFoo
接口声明意味着当您引用 IFoo
时,您可以对该引用调用 Foo
方法,但仅此而已。
IFoo = interface
procedure Foo;
end;
当 class 实现接口时,它必须实现该接口的所有方法。 IFoo
中的方法 Foo
将映射到 TFoo
中的方法 Foo
或 TOtherFoo
。具体接口的实现在不同的classes.
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>
声明为匿名函数。
ISmartPointer<T> = reference to function: T;
以上声明等同于带有 Invoke
函数的接口
ISmartPointer<T> = interface
function Invoke: T;
end;
两者之间的区别(我们在这里感兴趣的)是匿名 function/methods 你不必显式调用 Invoke
;编译器会为你做的。
由于ISmartPointer<T>
是一个匿名函数,在TSmartPointer<T>
的声明中实际上是一个接口class,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
函数调用时 returns 来自 FValue
和 [=] 的 TPerson
实例36=]有编译器可以识别的Name
属性。
由于 TSmartPointer<T>
是引用计数 class,当您使用 ISmartPointer<T>
引用时,基础 TSmartPointer<T>
对象实例以及 T
它包含在 FValue
中的实例将在 ISmartPointer<T>
引用超出范围时自动释放,或者您在代码中将其设置为 nil
。