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