QString 中智能指针实现

在 Delphi 中要实现智能指针,实际上是一个挺麻烦的事。QString.pas 单元的 TQPtr 封装了一个智能指针的实现,它和一般的智能指针实现一样,利用了接口的自动管理特性。我们先看一下它的声明:

  IQPtr = interface(IInterface)
    function Get: Pointer;
  end;

  TQPtrFreeEvent=procedure (AData:Pointer) of object;
  TQPtrFreeEventG=procedure (AData:Pointer);
  {$IFDEF UNICODE}
  TQPtrFreeEventA=reference to procedure (AData:Pointer);
  {$ENDIF}
  TQPtr = class(TInterfacedObject, IQPtr)
  private
    FObject: Pointer;
    FOnFree:TQPtrFreeEvent;
    {$IFDEF UNICODE}
    FOnFreeA:TQPtrFreeEventA;
    {$ENDIF}
  public
    constructor Create(AObject: Pointer); overload;
    destructor Destroy; override;
    class function Bind(AObject: TObject): IQPtr;overload;
    class function Bind(AData:Pointer;AOnFree:TQPtrFreeEvent):IQPtr;overload;
    class function Bind(AData:Pointer;AOnFree:TQPtrFreeEventG):IQPtr;overload;
    {$IFDEF UNICODE}
    class function Bind(AData:Pointer;AOnFree:TQPtrFreeEventA):IQPtr;overload;
    {$ENDIF}
    function Get: Pointer;
  end;

在这里,我们主要用利用其中的类函数 Bind 来完成对象或其它类型的指针到接口的封装。

TQPtr 内部实现了对 TObject 类型的自动释放处理,但对于结构体或者说是记录,由于复杂的结构体包含了成员的释放等额外的操作,而结构体也没有析构函数来完成这一切,所以需要提供一个释放函数来决定如何释放这个指针指向的数据。这里有三个重载分别对应类成员函数、全局函数和匿名函数版本,我们以其中一个版本为例来演示这一点。

首先,调用 TQPtr.Bind 来返回一个 IQPtr 接口,对于类的实例 AObject,我们只需要如下代码即可:

var
  IObj:IQPtr;
beegin
IObj:=TQPtr.Bind(AObject);
end;

对于记录类型指针 MyRecordPtr (假设类型是 PMyRecord),我们需要提供一个释放函数:

var
 IRec:IQPtr;
begin
IRec:=TQPtr.Bind(MyRecordPtr,
  procedure (AData:Pointer)
  begin
  Dispose(PMyRecord(AData));
  end);
end;

接下来,我们在使用时,怎么将它转换回对象或记录指针呢?假设我们将上面的IObj给一个函数 Foo 做为参数:

function Foo(IObj:IQPtr);
var
  AObj:TObject;
begin
AObj:=IObj.Get;
...
end;

对于记录,我们记录的代码和上面几乎没有区别:

function Foo(IRec:IQPtr);
var
  ARec:PMyRecord;
begin
ARec:=IRec.Get;
...
end;

OK,这就是 QString 提供的智能指针封装的实现。

有人寻求基于泛型的智能指针实现,实际上基于泛型不是不可以,但就只能对对象进行管理了,而上面可以对结构或其它自己分配的指针进行管理。

下面提供一个基于泛型的简单实现:

...
  ISmartPtr<PType:class>=interface
    function Get:PType;
  end;
  TSmartPtr<PType:class>=class(TInterfacedObject,ISmartPtr<PType>)
  protected
    FObject:PType;
  public
    constructor Create(AObj:PType);overload;
    destructor Destroy;override;
    function Get:PType;
  end;
...
{ TSmartPtr<PType> }

constructor TSmartPtr<PType>.Create(AObj: PType);
begin
inherited Create;
FObject:=AObj;
end;

destructor TSmartPtr<PType>.Destroy;
begin
if Assigned(FObject) then
  FreeAndNil(FObject);
inherited;
end;

function TSmartPtr<PType>.Get: PType;
begin
Result:=FObject;
end;

这样就是强类型的了,用法示例如下:

var
  IList:ISmartPtr<TStringList>;
begin
IList:=TSmartPtr<TStringList>.Create(TStringList.Create);
IList.Get.Add('Hello,world');
end;

各取所需吧。

分享到: