c++oding="utf-8" ?>
必须在main函数开头立即调用_crtsetdbgflag(_crtdbg_alloc_mem_df | _crtdbg_leak_check_df),配合_crtdbg_map_alloc宏、头文件及/mdd或/mtd调试运行时库,泄漏报告才有效输出到vs“输出”窗口的“调试”选项卡。

Windows下用_CrtSetDbgFlag开启堆调试
Visual Studio 的 CRT 库自带轻量级内存泄漏检测,不需要额外工具,但必须在程序启动早期启用,且只对 malloc、new 等 CRT 分配的内存有效。常见错误是把它放在 main 函数末尾或类构造里——这时候已经晚了。
正确做法是在 main 开头立即调用:
#include <crtdbg.h>
int main() {
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
// 其余代码...
}</crtdbg.h>
- 必须包含
<crtdbg.h></crtdbg.h>,否则编译报错_CrtSetDbgFlag未声明 - 链接时确保使用 Debug 版 CRT(即项目属性 → C/C++ → 代码生成 → 运行库选
/MDd或/MTd) - 输出泄漏信息默认打印到“输出”窗口(不是控制台),若看不到,请检查是否启用了“调试”输出通道
Linux/macOS下用Valgrind检查new/delete不匹配
Valgrind 的 memcheck 是跨平台事实标准,但它无法直接识别 C++ 对象生命周期,只能跟踪 malloc/free 级别分配。所以 new 漏删、delete 错用成 delete[]、或 std::vector 内部缓冲区泄漏,它都能报,但堆栈信息可能停在 operator new 而非你写的 new 行。
运行命令要加 --leak-check=full 和 --show-leak-kinds=all:
valgrind --leak-check=full --show-leak-kinds=all ./myapp
- 必须用未优化的二进制(
-O0 -g编译),否则行号丢失、内联函数堆栈混乱 - 如果程序用
fork(),加--trace-children=yes,否则子进程泄漏不显示 - 注意 false positive:某些第三方库(如 glibc 的 name resolution)会故意缓存内存,可加
--suppressions=.../default.supp过滤
RAII失效时如何定位裸指针泄漏点
现代 C++ 推荐用 std::unique_ptr 和 std::shared_ptr,但遗留代码或性能敏感路径仍存在裸指针(T*)+ new 组合。这类泄漏最难查,因为没 RAII 帮你兜底,且 Valgrind 报告里只显示“still reachable”或“definitely lost”,不提示哪次 new 没配 delete。
实用策略是打桩拦截:
- 重载全局
operator new和operator delete,记录分配地址、大小、调用栈(用backtrace()或__builtin_frame_address(0)) - 在程序退出前遍历所有未释放地址,用
addr2line -e ./myapp <addr></addr>反查源码行 - 更简单的方式:用 GCC/Clang 的
-fsanitize=address编译,运行时报错直接带泄漏位置,但仅限于“use-after-free”和“heap-buffer-overflow”,对纯泄漏需配合ASAN_OPTIONS=detect_leaks=1
std::shared_ptr循环引用导致的隐性泄漏
这是最容易被忽略的泄漏类型:两个对象用 std::shared_ptr 互相持有,引用计数永远不为 0,析构函数根本不会执行。Valgrind 会报 “still reachable”,而 CRT 调试器甚至不报——因为它没调用 operator delete,对象内存还在,只是逻辑上该死了。
检查方法很直接:
- 用
std::weak_ptr替换其中一个方向的引用(比如观察者模式中,被观察者持shared_ptr,观察者持weak_ptr) - 在关键对象析构函数里加日志,确认是否被调用;若没调用,再查 shared_ptr 持有链
- Clang/GCC 编译时加
-D_GLIBCXX_DEBUG(libstdc++)或-D_LIBCPP_DEBUG=1(libc++),部分实现会在循环引用时触发断言
真正麻烦的是跨模块或 DLL 边界的 shared_ptr 传递——不同模块可能用不同堆,shared_ptr 析构时 delete 会崩,这种泄漏往往伴随 crash,而不是安静地留在内存里。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











