第1讲:彻底解决C++中资源泄露
一、需要解决的问题
1. 内存泄露
2. 文件句柄泄露(文件打开后未关闭)
3. 锁未释放(加锁后未解锁)
4. 循环中忘记continue
5. 抛出异常后,资源未释放
6. 一对函数必须配合使用时,不小心导致未配对
二、解决方案
#ifndef XJ_DEFER
#define XJ_DEFER(name, func) auto name = std::unique_ptr<void, std::function<void(void*)>>(nullptr, [&](void*){func;});
#endif
XJ_DEFER 宏的核心原理是利用 std::unique_ptr 的 “资源自动释放” 特性,通过自定义删除器在对象离开作用域时执行清理逻辑。这种方式能有效避免因人为疏忽(如忘记释放、分支跳转遗漏)导致的资源泄露问题,尤其适合管理内存、句柄、锁、文件等需要手动释放的资源。
三、示例说明
1. 解决内存泄露(new 后忘记 delete)
问题代码:使用 new 分配内存后,因分支跳转或异常导致忘记 delete,造成内存泄露。
void func() {int* ptr = new int(10); // 分配堆内存if (some_condition) {return; // 此处返回,导致 ptr 未释放,内存泄露}// ... 其他逻辑delete ptr; // 正常路径才释放,异常/分支跳转时遗漏
}
改进代码:用 XJ_DEFER 在内存分配后绑定释放逻辑,作用域结束时自动执行 delete。
void func() {int* ptr = new int(10);XJ_DEFER(defer_delete, delete ptr); // 绑定释放逻辑,作用域结束时自动执行if (some_condition) {return; // 即使返回,defer_delete 会在作用域结束时释放 ptr}// ... 其他逻辑// 无需手动 delete,defer 会自动处理
}
2 解决句柄泄露(如 Windows 句柄未关闭)
问题代码:获取系统句柄(如文件句柄)后,因逻辑遗漏未关闭,导致句柄泄露。
#include <windows.h>void func() {HANDLE hFile = CreateFileA("test.txt", GENERIC_READ, 0, nullptr, OPEN_EXISTING, 0, nullptr);if (hFile == INVALID_HANDLE_VALUE) {return; // 句柄无效时返回,无需处理}if (some_condition) {return; // 此处返回,hFile 未关闭,句柄泄露}// ... 其他逻辑CloseHandle(hFile); // 正常路径才关闭,分支跳转时遗漏
}
改进代码:用 XJ_DEFER 绑定句柄关闭逻辑,确保作用域结束时释放。
#include <windows.h>void func() {HANDLE hFile = CreateFileA("test.txt", GENERIC_READ, 0, nullptr, OPEN_EXISTING, 0, nullptr);if (hFile == INVALID_HANDLE_VALUE) {return;}XJ_DEFER(defer_close, CloseHandle(hFile)); // 绑定句柄关闭逻辑if (some_condition) {return; // 即使返回,defer_close 会自动关闭 hFile}// ... 其他逻辑// 无需手动 CloseHandle,defer 会自动处理
}
3. 解决锁未释放
问题代码:手动锁定互斥锁后,因异常或分支跳转忘记解锁,导致死锁。
#include <mutex>std::mutex mtx;void func() {mtx.lock(); // 手动加锁if (some_condition) {return; // 此处返回,mtx 未解锁,其他线程会死锁}// ... 其他逻辑mtx.unlock(); // 正常路径才解锁,分支跳转时遗漏
}
改进代码:用 XJ_DEFER 绑定解锁逻辑,确保作用域结束时释放锁。
#include <mutex>std::mutex mtx;void func() {mtx.lock();XJ_DEFER(defer_unlock, mtx.unlock()); // 绑定解锁逻辑if (some_condition) {return; // 即使返回,defer_unlock 会自动解锁}// ... 其他逻辑// 无需手动 unlock,defer 会自动处理
}
4. 解决文件打开未关闭(FILE* 未关闭)
问题代码:用 fopen 打开文件后,因逻辑遗漏未调用 fclose,导致文件描述符泄露。
#include <cstdio>void func() {FILE* fp = fopen("test.txt", "r");if (!fp) {return;}if (some_condition) {return; // 此处返回,fp 未关闭,文件描述符泄露}// ... 其他逻辑fclose(fp); // 正常路径才关闭,分支跳转时遗漏
}
改进代码:用 XJ_DEFER 绑定文件关闭逻辑,确保作用域结束时关闭。
#include <cstdio>void func() {FILE* fp = fopen("test.txt", "r");if (!fp) {return;}XJ_DEFER(defer_fclose, fclose(fp)); // 绑定文件关闭逻辑if (some_condition) {return; // 即使返回,defer_fclose 会自动关闭 fp}// ... 其他逻辑// 无需手动 fclose,defer 会自动处理
}
5. 解决循环中忘记 continue 导致的资源未释放
问题代码:循环中满足条件时应跳过后续逻辑(continue),但忘记写 continue,导致资源重复处理或泄露。
#include <vector>
#include <cstdio>void func(const std::vector<int>& data) {for (int val : data) {FILE* fp = nullptr;if (val < 0) {fp = fopen("negative.txt", "a");if (!fp) continue;// ... 处理负数逻辑// 忘记写 continue,导致后续“正数逻辑”错误执行} else {fp = fopen("positive.txt", "a");if (!fp) continue;// ... 处理正数逻辑}fclose(fp); // 若上面忘记 continue,可能导致 fp 被重复使用或未关闭}
}
改进代码:用 XJ_DEFER 确保文件在当前循环迭代中自动关闭,即使忘记 continue,也不会泄露文件句柄。
#include <vector>
#include <cstdio>void func(const std::vector<int>& data) {for (int val : data) {FILE* fp = nullptr;if (val < 0) {fp = fopen("negative.txt", "a");if (!fp) continue;XJ_DEFER(defer_neg_close, fclose(fp)); // 绑定当前 fp 的关闭逻辑// ... 处理负数逻辑// 即使忘记写 continue,离开 if 作用域时,defer_neg_close 会自动关闭 fp} else {fp = fopen("positive.txt", "a");if (!fp) continue;XJ_DEFER(defer_pos_close, fclose(fp)); // 绑定当前 fp 的关闭逻辑// ... 处理正数逻辑}// 无需手动 fclose,defer 会在对应作用域结束时自动处理}
}
