動態建立一組按鈕(或其他控制項),常見做法用 TList 存指標,但釋放時 TList::Clear() 僅清空指標陣列,**不會 delete 物件**,導致記憶體洩漏。本文對比兩種正確釋放方案。
// 表單成員
TButton** Buttons = nullptr;
int BtnCount = 0;
void __fastcall TForm1::CreateButtons(int count) {
Buttons = new TButton*[count]; // 1. 分配指標陣列
for (int i = 0; i < count; ++i) {
Buttons[i] = new TButton(this); // 2. 逐個建立控制項
Buttons[i]->Parent = this;
Buttons[i]->SetBounds(10, 10 + i*30, 90, 25);
Buttons[i]->Caption = "Btn " + IntToStr(i);
}
BtnCount = count;
}
void __fastcall TForm1::FreeButtons() {
for (int i = 0; i < BtnCount; ++i)
delete Buttons[i]; // 1. 刪除每個控制項
delete[] Buttons; // 2. 刪除指標陣列
Buttons = nullptr;
BtnCount = 0;
}
TList* Buttons = new TList();
void __fastcall TForm1::CreateButtons(int count) {
for (int i = 0; i < count; ++i) {
TButton* btn = new TButton(this);
btn->Parent = this;
btn->SetBounds(10, 10 + i*30, 90, 25);
btn->Caption = "Btn " + IntToStr(i);
Buttons->Add(btn);
}
}
void __fastcall TForm1::FreeButtons() {
for (int i = 0; i < Buttons->Count; ++i) {
TButton* btn = static_cast(Buttons->Items[i]);
delete btn; // 必須顯式 delete
}
Buttons->Clear(); // 清空指標陣列
delete Buttons; // 刪除 TList 本身
Buttons = nullptr;
}
原理:TList::Items 為 void*,需 static_cast 才能呼叫解構函數。也可轉 TControl* 或 TComponent*(VCL 根基類)。
| 維度 | 雙重指標 | TList |
|---|---|---|
| 效能 | 極高(連續記憶體、無額外開銷) | 稍低(動態擴容、介面呼叫) |
| 記憶體佔用 | 最小 | 略大(內部緩衝區) |
| 功能豐富度 | 僅索引存取 | 排序、搜尋、插入、刪除、迭代器 |
| 程式碼複雜度 | 需手動管理兩層 new/delete | 語意清晰、符合 VCL 慣用模式 |
| 型別安全 | 編譯期保證 | 執行期轉型,需開發者保證型別正確 |
| 擴充性 | 固定大小,擴容需手動 realloc | 動態增減,自動擴容 |
C++11 以降,優先使用標準容器 + 智慧指標:
#include
#include
std::vector> Buttons;
void CreateButtons(int count) {
Buttons.reserve(count);
for (int i = 0; i < count; ++i) {
auto btn = std::make_unique(this);
btn->Parent = this;
btn->SetBounds(10, 10 + i*30, 90, 25);
btn->Caption = "Btn " + IntToStr(i);
Buttons.push_back(std::move(btn));
}
}
// 無需手動釋放,vector 析構自動釋放 unique_ptr,進而 delete TButton
優點:零記憶體洩漏、異常安全、語法簡潔、標準化。舊專案逐步遷移即可。