Delphi 中 virtual 與 override 是實現多態的核心機制。本文通過一個完整可編譯的範例,詳細演示 override 如何改變方法調用行為,對比虛擬方法與非虛擬方法在繼承鏈中的不同表現,並展示類型轉換對調用的影響。
unit Unit1;
interface
uses
Classes, Controls, Forms, Dialogs, StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
end;
{ 基類 }
TBase = class
procedure Proc1; virtual; { 虛擬方法 }
procedure Proc2; { 靜態方法 }
end;
{ 子類 }
TChild = class(TBase)
procedure Proc1; override; { 覆寫虛擬方法 }
procedure Proc2; { 重新宣告靜態方法(隱藏) }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
{ TBase 實現 }
procedure TBase.Proc1;
begin
ShowMessage('B1');
end;
procedure TBase.Proc2;
begin
ShowMessage('B2');
end;
{ TChild 實現 }
procedure TChild.Proc1;
begin
// inherited; { 可選:呼叫基類實現 }
ShowMessage('C1');
end;
procedure TChild.Proc2;
begin
ShowMessage('C2');
end;
{ 測試按鈕 }
procedure TForm1.Button1Click(Sender: TObject);
var
B: TBase;
C: TChild;
BC: TBase;
begin
B := TBase.Create;
C := TChild.Create;
BC := TChild.Create; { 父類變數持有子類實例 }
{ 直接呼叫 }
B.Proc1; // B1
B.Proc2; // B2
{ 類型轉換呼叫 }
TChild(B).Proc1; // B1 —— B 實際是 TBase,轉型不改變實際類型
TChild(B).Proc2; // C2 —— 靜態方法按變數類型綁定,TChild.Proc2
C.Proc1; // C1 —— 虛擬方法多態
C.Proc2; // C2 —— 靜態方法按實際類型(此處同)
TBase(C).Proc1; // C1 —— 虛擬方法多態,實際類型 TChild
TBase(C).Proc2; // B2 —— 靜態方法按變數類型 TBase
BC.Proc1; // C1 —— 虛擬方法多態
BC.Proc2; // B2 —— 靜態方法按變數類型 TBase
TBase(BC).Proc1; // C1
TBase(BC).Proc2; // B2
TChild(BC).Proc1; // C1
TChild(BC).Proc2; // C2
B.Free;
C.Free;
BC.Free;
end;
end.
| 呼叫代碼 | 輸出 | 說明 |
|---|---|---|
B.Proc1 | B1 | 基類實例呼叫虛擬方法 |
B.Proc2 | B2 | 基類實例呼叫靜態方法 |
TChild(B).Proc1 | B1 | 轉型不改變實際物件,虛擬方法仍按實際類型 |
TChild(B).Proc2 | C2 | 靜態方法按變數聲明類型綁定(TChild) |
C.Proc1 | C1 | 子類實例虛擬方法多態 |
C.Proc2 | C2 | 子類實例靜態方法 |
TBase(C).Proc1 | C1 | 虛擬方法多態:實際類型 TChild |
TBase(C).Proc2 | B2 | 靜態方法按變數類型 TBase |
BC.Proc1 | C1 | 父類變數持有子類實例,虛擬方法多態 |
BC.Proc2 | B2 | 靜態方法按變數類型 TBase |
TBase(BC).Proc1 | C1 | 虛擬方法多態 |
TBase(BC).Proc2 | B2 | 靜態方法按 TBase |
TChild(BC).Proc1 | C1 | 虛擬方法多態 |
TChild(BC).Proc2 | C2 | 靜態方法按變數類型 TChild |
virtual/override):運行時根據實際物件類型分派(動態綁定),實現多態。virtual):編譯時根據變數聲明類型綁定(靜態綁定),類型轉換會改變調用目標。override,屬於「隱藏」,不影響父類虛擬方法表。inherited:在 override 方法中呼叫 inherited 可執行父類實現,省略參數表時自動傳遞當前參數。| 錯誤代碼 | 現象 | 修正 |
|---|---|---|
忘記 override | 以為多態實則靜態綁定 | 子類方法必須加 override |
父類無 virtual | 編譯錯誤「Only virtual methods can be overridden」 | 父類方法加 virtual 或子類改用 reintroduce |
建構子未 override | 多態創建失敗 | constructor Create(...); override; |
解構子未 override | 釋放時未呼叫子類清理代碼 | destructor Destroy; override; |
virtual。override,編譯器會檢查簽名一致性。sealed(Delphi 10.4+)禁止進一步覆寫:procedure Proc1; override; sealed;。深入理解 override 與靜態綁定的差異,是編寫正確、可擴展 Delphi 物件導向代碼的關鍵。建議在代碼審查中重點檢查虛擬方法覆寫的完整性。