上文《BCB中使用VCL控件数组1》中,使用 TList 存放動態建立的 TSpeedButton,嘗試用 TList::Clear() 釋放資源失敗。原因在於 Clear() 僅將內部指標陣列歸零,**不會呼叫物件的解構函數**。
void __fastcall TForm1::FreeSpeedButtons() {
int n = Buttons->Count;
for (int i = 0; i < n; ++i) {
// 關鍵:void* → TSpeedButton* 強制轉型,然後 delete
delete static_cast(Buttons->Items[i]);
}
Buttons->Clear(); // 清空指標陣列
delete Buttons; // 釋放 TList 容器本身
Buttons = nullptr;
}
TSpeedButton → TButtonControl → TControl → TComponent → TPersistent → TObject。TObject 為所有 VCL 類別的共同基類,擁有虛擬解構函數。delete static_cast(ptr) 即可正確調用完整解構鏈(派生 → 基類)。TControl*、TComponent* 等中間基類指標皆可,只要確保指向完整物件。| 方案 | 優點 | 缺點 | 適用場景 |
|---|---|---|---|
雙重指標 TButton** | 效能最高、記憶體最省、編譯期型別安全 | 手動管理兩層記憶體、擴容麻煩、多重指標難讀 | 高效能、固定數量、底層庫開發 |
| TList + 強制轉型 | 語意清晰、動態增減、支援排序/搜尋、VCL 風格 | 略慢、執行期轉型風險、需手動 delete 物件 | 一般業務邏輯、數量變動、快速開發 |
| std::vector<unique_ptr> | 現代 C++ 標準、零洩漏、異常安全、無需手動 delete | 需 C++11 支援、舊編譯器不適用 | 新專案、長期維護、跨平台 |
Buttons->Clear(); delete Buttons; —— **漏掉物件 delete,記憶體洩漏**delete Buttons->Items[i]; —— **void* delete 未定義行為**delete static_cast(Buttons->Items[i]); —— **正確**Buttons->Clear() 避免懸空指標殘留TList 存放 VCL 控制項指標時,**必須逐項強制轉型後 delete**,再 Clear() 容器。這是「C++Builder 風格」的妥協:犧牲些許效能與型別安全,換取開發便利與 VCL 生態一致性。新專案請毫猶豫遷移至 std::vector。