在 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;
各取所需吧。