C++ 缺乏內建 GC,手動 new/delete 易導致記憶體洩漏或雙重釋放。本文設計一套「引用計數 + 循環引用偵測」混合機制:CPtrManager 統管所有堆上物件引用計數,auto_ptr 智慧指標自動增減計數;當離開作用域後殘留計數>0 物件,啟動背景執行緒掃描「互鎖群組」(所有智慧指標皆位於受管物件內部),強制釋放。並引入 CMutex 實現執行緒安全,完整程式碼可直接整合至專案。
| 類別 | 職責 |
|---|---|
CPtrManager | 全域單例,管理 CPtr 陣列、引用計數、標記分配、循環掃描執行緒 |
CPtr | 包裝單一堆物件:原始指標、大小、引用計數、擁有者智慧指標陣列與標記 |
auto_ptr<T> | 智慧指標模板:建構/複製/賦值/析構自動增減計數,支援 parent_autoptr 介面供強制釋放 |
CMark / CMarkTable | 掃描階段快照:拷貝 CPtr 資訊、識別「全在堆上」物件、遞迴剔除假鎖、分組釋放 |
void* operator new(size_t sz) { return thePtrManager.MallocPtr(sz); }
void operator delete(void* p) {
int mark = thePtrManager.GetMarkFromPtr(p);
if (mark > 0) thePtrManager.UserDelete(mark);
free(p);
}
template<class T> auto_ptr<T>::auto_ptr(T* ptr) {
pointee = ptr;
mark = thePtrManager.GetMarkFromPtr(ptr);
if (mark > 0) thePtrManager.AddCount(mark, pointee);
}
template<class T> void auto_ptr<T>::Remove() {
if (pointee && thePtrManager.Release(mark, pointee)) {
if (thePtrManager.GetCount(mark, pointee) == 0) delete pointee;
}
pointee = null; mark = 0;
}
int CPtrManager::GetAutoMark(void* smartPtr) {
for (CPtr* cp : m_ptr) {
int base = (int)cp.GetPtr();
int limit = base + cp.GetPtrSize();
if ((int)smartPtr >= base && (int)smartPtr <= limit) return cp.m_mark;
}
return 0; // 在棧上
}
原理:若智慧指標地址落在某受管物件記憶體區間內,則該智慧指標「內嵌」於該物件中。
CMarkTable.CopyAllMark() 拷貝所有 CPtr 至 CMark,避免鎖住主管理器。GetLockMark() 篩選「所有擁有者智慧指標皆在堆上」的 CMark 進入 CLockMarkArray。RemoveUnlockMark() 反覆移除「擁有者包含非候選集智慧指標」的項目,直至收斂。RemoveGroup(automark) 遞迴標記相連群組,最後呼叫 CMark.Release() → parent_autoptr.Release() → auto_ptr.Remove() 強制釋放。CPtrManager.mutex 保護全域結構(配置、計數增減、掃描快照)。CPtr.mutex 保護單一物件的擁有者陣列操作。THREAD_PRIORITY_ABOVE_NORMAL 執行,透過 bWrite 旗標協調寫入鎖。SmartPtr.h/cpp 加入專案。CWinApp 派生類中宣告全域 CPtrManager thePtrManager; 確保最後析構。new Type 回傳指標直接指派給 auto_ptr<Type>。auto_ptr 接收/傳遞;僅借用 → 繼續用裸指標。CPtrManager 全域鎖成為瓶頸;大量短命物件頻繁觸發掃描影響延遲;需編譯器支援 RTTI 與例外處理。std::shared_ptr + std::weak_ptr 手動打破循環,或引入 Clang Static Analyzer 靜態檢測。