很多初學者嘗試在 `OnDelete()` 中直接 `delete pObj`,發現文件大小不變、數據未真正移除。根本原因:**`delete` 僅釋放 C++ 堆內存,不會自動修改已序列化的磁盤文件**。MFC 序列化機制是「整文件覆寫」,單獨刪除對象無法原地修改文件結構。
在數據模型中增加 `bool m_bDeleted` 標記字段,刪除時僅置 `true`,序列化時跳過該標記對象。
// 模型類
class CMyDoc : public CDocument {
struct Item {
CString name;
bool deleted = false; // 新增標記
void Serialize(CArchive& ar) {
ar << name << deleted;
}
};
CArray- m_items;
void DeleteItem(int index) {
if (index >= 0 && index < m_items.GetSize())
m_items[index].deleted = true; // 偽刪除
SetModifiedFlag();
}
void Serialize(CArchive& ar) override {
if (ar.IsStoring()) {
int count = 0;
for (auto& item : m_items) if (!item.deleted) ++count;
ar << count;
for (auto& item : m_items)
if (!item.deleted) item.Serialize(ar);
} else {
int count; ar >> count;
m_items.SetSize(count);
for (auto& item : m_items) item.Serialize(ar);
}
}
};
數據量小(< 10MB)、刪除頻率低、必須物理釋放磁盤空間。
void CMyDoc::OnDeleteItem(int index) {
if (index < 0 || index >= m_items.GetSize()) return;
m_items.RemoveAt(index); // 真正從內存移除
SetModifiedFlag();
// 可選:立即保存,或等用戶點「保存」
// OnSaveDocument(GetPathName());
}
| 維度 | 偽刪除 | 真刪除 |
|---|---|---|
| 實現難度 | 低(加字段+判斷) | 低(RemoveAt+Save) |
| 性能 | 極高 | 隨文件大小線性增長 |
| 撤銷支持 | 原生支持 | 需額外實現撤銷棧 |
| 磁盤空間 | 不自動釋放 | 即時釋放 |
| 推薦場景 | 大多數桌面應用 | 嵌入式/極小文件 |
在「文件 → 整理」菜單或退出程序時,遍歷內存數據,重新序列化一次,物理清除已標記對象:
void CMyDoc::Compact() {
CArray- alive;
for (auto& item : m_items) if (!item.deleted) alive.Add(item);
m_items = alive;
OnSaveDocument(GetPathName());
}
MFC 序列化本質是「整文件覆寫」,**單對象 `delete` 無法修改磁盤**。工程上首選偽刪除(標記法),配合定期壓縮,兼顧性能、可撤銷、實現簡單;僅在極小文件或強合規場景下採用真刪除(重寫文件)。理解這一機制,能避免在文檔類應用中走彎路。