2-3-3 发现内存泄露

2.3.3 发现内存泄露

本部分未被标准接收,仅作为一个技巧。

​ 做法:通过宏来重载new和new[]运算符。

​ 思路:

  • new运算符使用一个数组记录每一次申请的内存以及所在代码的文件和行数。

  • delete运算符遍历该内存是否存在于数组中,如果不在就显示错误,在则正常删除,并移除数组元素。

1.头文件

//MemCheck.h
#ifndef MEMCHECK_H
#define MEMCHECK_H
#include <cstddef> //std::size_t

void* operator new(std::size_t, const char*, long);
void* operator new[](std::size_t, const char*, long);
#define new new (__FILE__, __LINE__)

extern bool traceFlag;
#define TRACE_ON() traceFlag = true
#define TRACE_OFF() traceFlag = false

extern bool activeFlag;
#define MEM_ON() activeFlag = true
#define MEM_OFF() activeFlag = false

#endif

注意:当需要跟踪动态存储区活动时,在最后一个头文件包含该文件。

2.实现文件

文件结构:

  • 全局跟踪开关变量

  • 匿名空间

    • Info结构体
    • 全局内存管理数组
    • 查找和删除数组元素的api
    • 自动析构以检测内存泄露的全局变量Sentinel
  • 重载全局new和new[]

  • 重载全局delete和delete[]

//MemCheck.cpp
#include <cstudio>
#include <cstdlib>
#include <cassert>
#include <cstddef>
using namespace std;
#undef new

bool traceFlag = true; //print the new and delete trace if true
bool activeFlag = false; //print the new and delete exception trace if true

namespace {
//memory map entry type
struct Info{
void* ptr; //ptr to memory
const char* file; //file address
long line; //line number
};

//memory map data
const size_t MAXPTRS = 10000u;
Info memMap[MAXPTRS];
size_t nptrs = 0;

//Searchs the map for an address
int findPtr(void* p)
{
for(size_t i = 0;i<nptrs;i++)
{
if(memMap[i].ptr == p)
return i;
}
return -1;
}

//Del the remove the ptr from vector
void delPtr(void* p)
{
int pos = findPtr(p);
assert(pos >= 0);
for(size_t i = pos; i < nptrs - 1;i++)
{
memMap[i] = memMap[i+1];
}
--nptrs;
}

struct Sentinel {
~Sentinel(){
if(nptrs > 0)
{
printf("Leaked memory at:\n");
for(size_t i = 0;i < nptrs;i++)
{
printf("\t%p (file: %s, line: %ld)\n",
memMap[i].ptr, memMap[i].file, memMap[i].line);
}
}
else
printf("No user memory leaks!\n");
}
};

Sentinel s; //auto destory when the program exit
}//end of namespace



//Overload scalar new and delete
void* operator new(size_t siz, const char* file, long line)
{
void* p = malloc(siz);
if(activeFlag)
{
if(nptrs == MAXPTRS)
{
printf("memory map too small! (increase MAXPTRS)\n");
exit(1);
}
memMap[nptrs].ptr = p;
memMap[nptrs].file = file;
memMap[nptrs].line = line;
++nptrs;
}
}

void* operator new[](size_t siz, const char* file, long line)
{
return operator new(siz, file, line);
}

void operator delete(void* p)
{
if(findPtr(p) >= 0)
{
free(p);
assert(nptrs > 0);
delPtr(p);
if(traceFlag)
{
printf("delete memory at address %p\n", p);
}
else if(!p && activeFlag)
{
printf("Attempt to delete unknown pointer: %p\n", p);
}
}
}

void operator delete[](void* p)
{
operator delete(p);
}

3.使用

  • 作为最后一个头文件出现

  • 在main的开头使用MEM_ON()宏,结尾使用MEM_OFF()宏

#include "MemCheck.h"

int main()
{
MEM_ON();
...
MEM_OFF();
}