目录
学习练习用,真正检测内存泄漏最好还是 借助专门的工具。
C++语言实现
思路:
1.重载operator new/new[ ] 与 operator delete/delete[ ], 并借助双向链表结构(带头节点)管理内存,new的时候将 内存信息存入链表,delete的时候将内存信息踢出链表,程序结束后,查看链表剩余多少节点判断内存泄漏。
https://blog.csdn.net/ssopp24/article/details/77278439
new操作符是由C++语言内建的, 就像sizeof那样, 不能改变意义, 总是做相同的事情:
- 调用operator new (sizeof(A))
- 调用A:A()
- 返回指针
第一: 它分配足够的内存, 用来放置某类型的对象.
第二: 它调用一个构造函数, 为刚才分配的内存中的那个对象设定初始值。
第三: 对象被分配了空间并构造完成, 返回一个指向该对象的指针
第二、第三,new operator(即 new 操作符)总是做这两件事,无论如何你是不能改变其行为。
能够改变的是用来容纳对象的那块内存的分配行为, new operator(new)调用某个函数, 执行必要的内存分配动作, 你可以重写或者重载那个函数, 改变其行为. 这个函数名称就叫operator new 。
函数 operator new 通常声明如下:
void * operator new (size_t size);
其返回类型void*. 即返回一个指针, 指向一块原始的, 未设置初始值的内存。
函数中的size_t参数表示需要分配多少内存, 你可以将operator new 重载, 加上额外的参数, 但第一个参数类型必须总是size_t.
可以这样理解: new int -> new(sizeof(int)) -> operator new(sizeof(int)/*即size_t size*/)->重载.
不能改变关键字new的行为 但我们能重载operator new( size_t size )
注意: operator new( size_t size )中的参数size 是new 计算的. 不用我们自己计算. 我们重载时, 只需要开辟 size个字节的内存大小即可
LeakDetector.h:
// 注意, 我们的头文件是要被包含进被测试的.cpp 的, 所以头文件中不要出现"多余的"代码及库文件, 以免影响被测文件
#ifndef LEAK_DETECTOR_H_
#define LEAK_DETECTOR_H_
// 有个小技巧: C/C++库中标准的头文件宏定义是这种形式: _STDIO_H( 标准规定保留下划线作前缀 )
// 所以平时我们为了避免自己定义的宏意外地与标准头文件定义的宏发生冲突, 我们使用下划线作后缀, 并且不用下划线作前缀
// 重载版本: operator new/new[]( ), operator delete/delete[]( ) 的声明
void* operator new(size_t size, const char* file, size_t line);
void* operator new[](size_t size, const char* file, size_t line);
// 注意到, 上面我们重载的函数中, 第一个参数和第三个参数的类型是size_t
// 其中第一个参数size为 sizeof的返回值, 所以为size_t类型
// 第三个参数的含义为 行号, 是我们重载 operator new/new[]( )后自己加的参数, 此处也可以用
//unsigned int. 但最好用 size_t. 原因是size_t的可移植性好. 理由见上面链接
void operator delete(void* ptr);
void operator delete[](void* ptr);
// 这个宏在LeakDetector.cpp中定义, 使得编译时 源码中的new被替换为
//new( __FILE__, __LINE__ ),源码就变成了使用我们自己的重载版本
//operator new/new[]( size_t size, const char* file, size_t line )
#ifndef NEW_OVERLOAD_IMPLEMENTATION_
#define new new( __FILE__, __LINE__ )
// 预定义宏:
// __FILE__(两个下划线): 代表当前源代码文件名
// __LINE__(两个下划线): 代表当前源代码文件中的行号
#endif
class LeakDetector {
public:
// LeakDetector.cpp和被测试的.cpp都会包 LeakDetector.h头文件
// 因此两个源文件中会创建两个静态LeakDetector类对象 exitCounter (两个静态类对象名相同,
//但是它们的链接属性均为内链接(只在当前源文件有效), 因此不会重定义), 如果此时两个对析
//构, 会调用两次析构函数, 调用两次内存泄漏检测函数. 而我们的预期是只调用一次内存泄漏
//检测函数. 所以我们声明一个所有类对象共享的静态变量来实现我们的目的