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;
引入泛型約束 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 對應 | 行為 | 使用場景 |
|---|---|---|---|
Get | get() | 取得被管理物件參考,不轉移擁有權 | 呼叫方法/屬性 |
Release | release() | 取得物件並放棄擁有權,智能指針不再釋放 | 轉移給外部管理 |
Reset | reset() | 釋放舊物件,接管新物件 | 重新指向 |
| 析構 | ~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。
// 錯誤示範
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 → 不釋放 → 洩漏
解決:被管理物件絕不持有指向管理者的介面參考。若需反向引用,用弱引用模式 (手動管理不計入計數的指標)。
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); // 手動釋放
IAutoPtr<T> → TAutoPtr<T> → T,兩次指標解引用Delphi 2009 結合介面參考計數與泛型強型別,實現了TAutoPtr<T> 智能指針。核心優勢:自動釋放、強型別安全、集合無縫整合。必須警惕循環引用與提前釋放兩大陷阱,善用 Release 轉移擁有權。雖無運算子重載語法糖,但已大幅降低記憶體管理認知負擔,是 Delphi 現代化開發的重要基礎設施。