C++ 內存泄漏的五大致命陷阱!90% 程序員踩過的坑你中招了嗎?
C++中內存泄漏指程序未能正確釋放已動態分配的內存,導致內存資源浪費。常見的內存泄漏類型如下:
1. 未釋放動態內存
第一種情況:使用new分配內存后未調用delete釋放。
示例:
void func() {
int* ptr = new int(10);
// 忘記delete ptr
}
每次調用func()都會泄漏內存(常發性泄漏)。若函數被多次調用,內存耗盡風險高。
第二種情況 :異常導致內存未釋放,在 new 和 delete 之間拋出異常,導致 delete 未執行。
示例:
void risky() {
int* ptr = new int(42);
some_function_that_may_throw(); // 可能拋出異常
delete ptr; // 如果異常拋出,此行不會執行
}
修復:使用 RAII 或智能指針(如 std::unique_ptr)自動管理內存:
std::unique_ptr<int> ptr(new int(42));
//或者
auto ptr = std::make_unique<int>(42);
第三種情況:容器中的指針未釋放,容器存儲指針,但未在銷毀容器前釋放內存。
示例:
std::vector<int*> vec;
vec.push_back(new int(10));
vec.push_back(new int(20));
// 直接清空容器,但內存未釋放
vec.clear();
修復:手動釋放內存或者使用智能指針
for (auto ptr : vec) {
delete ptr;
}
vec.clear();
std::vector<std::unique_ptr<int>> vec;
vec.push_back(std::make_unique<int>(10)); // C++14
// 無需手動釋放,容器clear時自動釋放內存
2. 錯誤使用delete
第一種:數組未用delete[]
int* arr = new int[5]
delete arr; // 錯誤,應使用delete[] arr
導致數組元素未被完全釋放,可能引發未定義行為。
第二種:對void*指針使用delete
class Object { /* ... */ };
void* ptr = new Object();
delete ptr; // 錯誤,未調用Object析構函數
正確做法:轉換為原類型再delete。
3. 資源泄漏(文件 網絡 線程)
未釋放文件句柄、數據庫連接等系統資源。
示例:
void openFile() {
FILE* file = fopen("data.txt", "r");
// 忘記fclose(file);
}
資源泄漏可能間接導致內存問題。
修改:調用fclose或者使用下面的C++風格
void openFile() {
std::fstream file("data.txt");
// 無需手動關閉,析構時自動釋放
}
4. 未定義虛析構函數
基類指針指向派生類對象時,若基類析構函數非虛,派生類析構函數不會被調用。
示例:
class Base { public: ~Base() {} };
class Derived : public Base { private: int* data; };
Base* obj = new Derived();
delete obj; // 僅調用Base析構函數,Derived的data未釋放
解決方案:將基類析構函數聲明為virtual。
5. 循環引用(智能指針場景)
原因:shared_ptr相互引用導致引用計數無法歸零。
示例:
class A {
public:
shared_ptr b_ptr;
};
class B {
public:
shared_ptr a_ptr;
};
auto a = make_shared();
auto b = make_shared();
a->b_ptr = b;
b->a_ptr = a; // 循環引用,內存無法釋放
解決方案:使用weak_ptr打破循環。
將其中一個指針改為 std::weak_ptr:
class B {
public:
std::weak_ptr<A> a_weak_ptr; // 使用 weak_ptr 打破循環
};
如何避免內存泄漏?
- 優先使用智能指針:如 std::unique_ptr、std::shared_ptr 和 std::weak_ptr。
- 遵循 RAII 原則:將資源管理封裝在對象生命周期中(如文件句柄、鎖等)。
- 使用工具檢測泄漏:如 Valgrind、AddressSanitizer 等。