首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> Delphi

Delphi 2009 智能指針進階實作:支援指標型別、型別資訊釋放與自動構造的 AutoPtr 完整原始碼

     編輯:Delphi

從「智能物件」到真正的「智能指針」

首版實作將泛型約束限定為 T: class,僅能管理類別實例,實為「智能物件」。本文移除 class 約束,支援任意型別 (含指標),解決值型別無法賦 nil、不支援比較運算子、無統一釋放機制等核心難題,實現真正語意上的智能指針。

核心挑戰與解決方案

挑戰原因解決方案
fObj := nil 編譯錯誤T 可能為值型別Integer((@fObj)^) := 0 直接清零記憶體
aObj <> fObj 不支援泛型 T 不保證有比較運算子Integer((@aObj)^) <> Integer((@fObj)^) 比較地址
if fObj = nil 不支援值型別無 nil 概念if Integer((@fObj)^) <> 0 檢查地址非零
統一釋放機制T 可能是類別/指標/陣列用 TypeInfo(T) 運行期判斷 Kind 執行對應釋放邏輯

型別資訊驅動的釋放策略


procedure TAutoPtr<T>.FreeObj;
begin
  // 獲取泛型型別資訊
  if fTypeInfo = nil then
  begin
    // T 為無類型指標 (Pointer) - Kind = tkUnknown 或 nil
    Dispose(Pointer((@fObj)^)); // 內部含 _Finalize + _FreeMem
  end
  else
  case fTypeInfo.Kind of
    tkClass:
      TObject((@fObj)^).Free; // 呼叫虛擬析構函數鏈
    tkArray, tkDynArray:
      ; // 陣列/動態陣列編譯器自動管理,無需手動釋放
    // tkRecord, tkInterface 等其他類型可擴展
  end;
  // 統一清零
  Integer((@fObj)^) := 0;
end;

完整 AutoPtr 單元原始碼 (v0.2 Beta)


{*******************************************************
* Delphi Smart Pointer Class
* AutoPtr
* Version 0.2 Beta
* Yang Qinqing @ https://www.cnblogs.com/felixyeou
*******************************************************}
unit AutoPtr;

interface
uses SysUtils, TypInfo;

type
  IAutoPtr<T> = interface
    ['{86DB82D6-9A32-4A6A-9191-2E0DFE083C38}']
    function Get: T;
    function Release: T;
    procedure Reset(aObj: T);
  end;

  TAutoPtr<T> = class(TInterfacedObject, IAutoPtr<T>)
  private
    fObj: T;
    fTypeInfo: PTypeInfo;
    procedure FreeObj;
  public
    class function New(aObj: T): IAutoPtr<T>; overload;
    class function New: IAutoPtr<T>; overload; // 自動構造類別實例
    constructor Create(aObj: T); virtual;
    destructor Destroy; override;
    function Get: T;
    function Release: T;
    procedure Reset(aObj: T);
  end;

implementation

{ TAutoPtr<T> }

constructor TAutoPtr<T>.Create(aObj: T);
begin
  fObj := aObj;
  fTypeInfo := TypeInfo(T); // 緩存型別資訊
end;

class function TAutoPtr<T>.New(aObj: T): IAutoPtr<T>;
begin
  Result := TAutoPtr<T>.Create(aObj) as IAutoPtr<T>;
end;

function TAutoPtr<T>.Release: T;
begin
  Result := fObj;
  Integer((@fObj)^) := 0; // 清零,不釋放物件
end;

procedure TAutoPtr<T>.Reset(aObj: T);
begin
  if Integer((@aObj)^) <> Integer((@fObj)^) then
  begin
    FreeObj; // 釋放舊物件
    fObj := aObj; // 接管新物件
  end;
end;

destructor TAutoPtr<T>.Destroy;
begin
  if Integer((@fObj)^) <> 0 then FreeObj;
  fTypeInfo := nil;
  inherited;
end;

procedure TAutoPtr<T>.FreeObj;
begin
  if fTypeInfo = nil then
  begin
    // 無類型指標
    Dispose(Pointer((@fObj)^));
  end
  else
  case fTypeInfo.Kind of
    tkClass:
      TObject((@fObj)^).Free;
    tkArray, tkDynArray:
      ; // 自動管理
  end;
  Integer((@fObj)^) := 0;
end;

function TAutoPtr<T>.Get: T;
begin
  Result := fObj;
end;

class function TAutoPtr<T>.New: IAutoPtr<T>;
var
  typInfo: PTypeInfo;
  obj: TObject;
  objNew: T;
begin
  typInfo := TypeInfo(T);
  // 僅支援類別型別自動構造
  if (typInfo <> nil) and (typInfo.Kind = tkClass) then
  begin
    obj := GetTypeData(typInfo).ClassType.Create; // 呼叫預設建構函數
    objNew := T((@obj)^); // 強制轉型
    Exit(New(objNew));
  end;
  raise Exception.Create('只能構造 class 型別的物件。');
end;

end.

關鍵技術細節解析

1. 記憶體地址操作核心技巧


// 取得變數地址並解引用為 Integer 進行操作
Integer((@fObj)^) := 0;        // 相當於 fObj := nil (對指標/類別)
Integer((@aObj)^) <> Integer((@fObj)^) // 相當於 aObj <> fObj (比較指標值)
Integer((@fObj)^) <> 0         // 相當於 fObj <> nil

原理:@fObj 取得變數地址 (型別 ^T),(@fObj)^ 解引用得到原值,Integer(...) 強制視為整數進行位元級操作。這繞過了泛型約束限制,直接操作底層記憶體。

2. Dispose vs FreeMem 選擇


// System.pas 中 Dispose 實作 (簡化)
asm
  PUSH EAX
  CALL _Finalize  // 處理受管理型別 (介面、動態陣列、字串、Variant 等)
  POP EAX
  CALL _FreeMem   // 釋放記憶體塊
end;

選擇 Dispose:自動處理指向受管理型別指標的終結化 (如 PString、PInterface),比單純 FreeMem 更安全完整。

3. 自動構造 New 重載


class function TAutoPtr<T>.New: IAutoPtr<T>;
var typInfo: PTypeInfo; obj: TObject; objNew: T;
begin
  typInfo := TypeInfo(T);
  if (typInfo <> nil) and (typInfo.Kind = tkClass) then
  begin
    obj := GetTypeData(typInfo).ClassType.Create; // 獲取類別型別並構造
    objNew := T((@obj)^); // 類別實例指標轉為泛型 T
    Exit(New(objNew));
  end;
  raise Exception.Create('只能構造 class 型別的物件。');
end;

用法:var ptr := TAutoPtr<TMyClass>.New; 自動構造並接管,無需手動 Create。

使用範例

1. 管理類別實例 (原有功能)


var ap: IAutoPtr<TMyClass>;
ap := TAutoPtr<TMyClass>.New(TMyClass.Create);
ap.Get.DoSomething;

2. 管理指標型別 (新功能)


type PMyRecord = ^TMyRecord;
var ptr: IAutoPtr<PMyRecord>;
New(ptr.Get); // 分配記憶體
ptr.Get^.Field := 123;
// 離開作用域自動 Dispose

3. 自動構造 (新功能)


var ap: IAutoPtr<TMyClass>;
ap := TAutoPtr<TMyClass>.New; // 自動 Create
ap.Get.DoSomething;

4. 重置指向新物件


ap.Reset(TMyClass.Create); // 自動 Free 舊物件,接管新物件

5. 釋放擁有權


var obj := ap.Release; // 取回物件,智能指針不再管理
// 需手動 obj.Free

局限性與未來擴展方向

局限說明可能方案
不支援記錄型別 (Record) 管理Record 無建構/析構,通常棧分配若為堆上 Record 指標,歸屬指標管理
介面型別釋放未特殊處理介面有參考計數,Free 不適用擴展 tkInterface 分支:IInterface((@fObj)^) := nil
動態陣列/字串指標管理編譯器自動管理,但指標需 _FinalizeDispose 已處理 _Finalize
無運算子重載Delphi 不支援運算子重載無法實現 *ptr, ptr-> 語法糖
執行期型別檢查開銷每次釋放需 case Kind可用 RTTI 緩存或編譯期特化優化

總結

通過移除 class 約束、記憶體地址直接操作、TypeInfo 運行期型別分發、Dispose 統一釋放四大核心技術,本實作實現了支援類別、指標、自動構造的真正泛型智能指針 TAutoPtr<T>。雖受限於 Delphi 語言特性 (無運算子重載、無編譯期特化),但已大幅超越首版「智能物件」,接近 C++ std::unique_ptr 的語意與功能。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved