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

Delphi 2009 智能指針入門實作:基於介面參考計數與泛型的 AutoPtr 完整教學

     編輯:Delphi

背景:為什麼 Delphi 需要智能指針?

C++ 中物件可建立在堆疊 (自動析構) 或堆積 (手動 delete)。Delphi 早期物件模型僅支援堆積類別 (class),必須手動 Free,易造成記憶體洩漏。Delphi 2009 引入泛型後,結合介面參考計數自動釋放機制,終能實現類 C++ 智能指針。

核心原理:介面參考計數 = 自動析構


// 介面變數離開作用域 → 參考計數歸零 → 自動呼叫 _Release → 呼叫 Destroy
var Intf: IMyInterface;
Intf := TMyClass.Create; // 參考計數 = 1
// ... 使用 Intf ...
end; // 此處自動釋放 TMyClass 實例

關鍵:TInterfacedObject 實作 IInterface 的 _AddRef/_Release,_Release 歸零時呼叫 Destroy。

第一版:非泛型智能指針


type
  TClassicalAutoPtr = class(TInterfacedObject)
  private fObj: TObject;
  public
    constructor Create(aObj: TObject);
    destructor Destroy; override;
    class function New(aObj: TObject): IInterface;
  end;

constructor TClassicalAutoPtr.Create(aObj: TObject);
begin fObj := aObj; end;

destructor TClassicalAutoPtr.Destroy;
begin
  FreeAndNil(fObj); // 核心:析構時自動釋放被管理物件
  inherited;
end;

class function TClassicalAutoPtr.New(aObj: TObject): IInterface;
begin
  Result := TClassicalAutoPtr.Create(aObj); // 回傳介面,觸發參考計數
end;

第二版:泛型強型別智能指針 (AutoPtr)

引入泛型約束 T: class,提供 Get/Release/Reset 核心方法,模仿 C++ auto_ptr。

完整介面與實作


type
  IAutoPtr<T: class> = interface(IInterface)
    ['{BD098FDB-728D-4CAC-AE40-B12B8B556AD3}']
    function Get: T;
    function Release: T;
    procedure Reset(aObj: T);
  end;

  TAutoPtr<T: class> = class(TInterfacedObject, IAutoPtr<T>)
  private fObj: T;
  public
    class function New(aObj: T): IAutoPtr<T>;
    constructor Create(aObj: T); virtual;
    destructor Destroy; override;
    function Get: T;
    function Release: T;
    procedure Reset(aObj: T);
  end;

{ 實作 }
constructor TAutoPtr<T>.Create(aObj: T);
begin fObj := aObj; end;

class function TAutoPtr<T>.New(aObj: T): IAutoPtr<T>;
begin
  Result := TAutoPtr<T>.Create(aObj) as IAutoPtr<T>; // 關鍵:as 介面轉型
end;

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

function TAutoPtr<T>.Release: T;
begin
  Result := fObj;
  fObj := nil; // 放棄擁有權,不再自動釋放
end;

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

destructor TAutoPtr<T>.Destroy;
begin
  if fObj <> nil then FreeAndNil(fObj);
  inherited;
end;

核心方法語意對照表

方法C++ auto_ptr 對應行為使用場景
Getget()取得被管理物件參考,不轉移擁有權呼叫方法/屬性
Releaserelease()取得物件並放棄擁有權,智能指針不再釋放轉移給外部管理
Resetreset()釋放舊物件,接管新物件重新指向
析構~auto_ptr()自動釋放被管理物件作用域結束

實戰測試程式碼


type
  TTestClass = class
  private fInt: Integer;
  public
    constructor Create(aInt: Integer);
    destructor Destroy; override;
    procedure DoPrintInt;
  end;

procedure DoTestAutoPtr;
var ap: IAutoPtr<TTestClass>;
begin
  // 方式 1: Create 回傳介面 (需 as 轉型)
  ap := TAutoPtr<TTestClass>.Create(TTestClass.Create(10)) as IAutoPtr<TTestClass>;
  ap.Get.DoPrintInt; // 輸出 10

  // 方式 2: New 靜態方法 (推薦)
  ap := TAutoPtr<TTestClass>.New(TTestClass.Create(20));
  ap.Get.DoPrintInt;

  // Release 測試
  var obj := ap.Release; // 取得物件,ap 不再管理
  obj.DoPrintInt; // 仍可用
  obj.Free; // 必須手動釋放!
end;

進階:智能指針與泛型集合結合


var gList: TList<IAutoPtr<TTestClass>>;
var gAp: IAutoPtr<TTestClass>;

procedure DoTestAutoPtrList;
var ap: IAutoPtr<TTestClass>; n: Integer;
begin
  gList := TList<IAutoPtr<TTestClass>>.Create;
  try
    for n := 0 to 2 do
    begin
      ap := TAutoPtr<TTestClass>.New(TTestClass.Create(10));
      ap.Get.DoPrintInt;
      gList.Add(ap); // 集合持有介面參考,計數 +1
      ap := nil; // 區域變數釋放,計數 -1,但集合仍持有
    end;

    gAp := gList[1]; // 保存其中一個
    gList.Free; // 清空列表,其他釋放
    gAp := nil; // 最後釋放
  end;
end;

優勢:集合自動管理元素生命週期,無需手動遍歷 Free。

兩大經典陷阱與解決方案

陷阱 1:循環引用 → 記憶體洩漏


// 錯誤示範
type TTestClass = class
  public Ap: IAutoPtr<TTestClass>; // 物件持有指向自己智能指針的介面
end;

ap := TAutoPtr<TTestClass>.New(TTestClass.Create);
ap.Get.Ap := ap; // 循環引用:ap → obj → Ap → ap
// 離開作用域時,ap 參考計數 = 2 (區域變數 + obj.Ap)
// 區域變數釋放後計數 = 1 ≠ 0 → 不釋放 → 洩漏

解決:被管理物件絕不持有指向管理者的介面參考。若需反向引用,用弱引用模式 (手動管理不計入計數的指標)。

陷阱 2:提前釋放被管理物件內部資源


type TTestClass = class
  private fList: TList<Integer>;
  public constructor Create; ...
  destructor Destroy; override; // FreeAndNil(fList)
end;

gTt := TAutoPtr<TTestClass>.New(TTestClass.Create(10)).Get; // 取得物件
// 方法結束,智能指針釋放 → TTestClass.Destroy → fList 被 Free
gTt.DoPrintInt; // AV!fList 已被釋放

解決:若需延長物件壽命超過智能指針作用域,必須用 Release 取得擁有權:


gTt := TAutoPtr<TTestClass>.New(TTestClass.Create(10)).Release; // 取得擁有權
gTt.DoPrintInt; // 安全
FreeAndNil(gTt); // 手動釋放

效能與記憶體考量

  • 介面參考計數開銷:每次賦值/複製涉及原子操作 (InterlockedIncrement/Decrement),微小但可測
  • 雙重間接:IAutoPtr<T> → TAutoPtr<T> → T,兩次指標解引用
  • 現代 CPU 影響可忽略:相比手動 Free 風險,效能損失可接受
  • 無循環引用時:記憶體安全性極高,接近 GC 語言體驗

總結

Delphi 2009 結合介面參考計數與泛型強型別,實現了TAutoPtr<T> 智能指針。核心優勢:自動釋放、強型別安全、集合無縫整合。必須警惕循環引用與提前釋放兩大陷阱,善用 Release 轉移擁有權。雖無運算子重載語法糖,但已大幅降低記憶體管理認知負擔,是 Delphi 現代化開發的重要基礎設施。

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