Lyt
posts - 16,comments - 61,trackbacks - 0

 在编程中经常涉及到内存管理,于是便希望有个内存检测器来帮助我们debug。我们想检测内存泄漏,就必须记录程序中的内存分配和释放情况,同时我们也希望能记录内存分配代码所在行号和位置,也就是我们需要重载以下四个全局函数:

void* operator new(size_t Size, char* FileName, int LineNum);
void* operator new[](size_t Size, char* FileName, int LineNum);
void operator delete(void* Object);
void operator delete[](void* Object);

下面将描述如何实现一个简单的内存检测器

1.获得内存分配代码所在行号和位置

我们需要将缺省的全局new operator替换为带有文件名和行号的版本,代码如下:

#define new new(__FILE__, __LINE__ )

2.建立记录内存分配和释放情况的数据结构NewList

NewList中记录了内存分配代码所在行号、位置、内存分配后获得指针与大小。由于我们想记录整个程序中内存分配和释放情况,于是应实例化一个NewList全局对象NewRecord。

注意:全局对象应放在cpp中,如果放在.h中,.h文件又被多个文件include,于是会出现重定义

3.重载new和delete那四个全局函数

new:(1)分配内存,如果失败了抛出异常 (2)记录相关数据 (3)返回分配所得指针

注意在重载operator new[]时Size为0的情况,这时我们当Size=1;

delete :(1)找到相关数据并删除 (2)释放内存

void* operator new(size_t Size, char* FileName, int LineNum)
{
    
if (Size==(size_t)0) Size=1;
    
void* Result=::operator new(Size);
    
if (!Result) throw bad_alloc();
    
else
    
{
        NewListNode Temp(Result, Size, FileName, LineNum);
        NewRecord.Add(Temp);
        
return Result;
    }

}


void* operator new[](size_t Size, char* FileName, int LineNum)
{
    
if (Size==(size_t)0) Size=1;
    
void* Result=::operator new[](Size);
    
if (!Result) throw bad_alloc();
    
else
    
{
        NewListNode Temp(Result, Size, FileName, LineNum);
        NewRecord.Add(Temp);
        
return Result;
    }

}


void operator delete(void* Object)
{
    
if (!Object) return;
    NewRecord.Remove(Object);
    free(Object);
}


void operator delete[](void* Object)
{
    
if (!Object) return;
    NewRecord.Remove(Object); 
    free(Object);
}

4.如何打印出内存泄漏的有关信息

对于打印信息我们会有各种各样不同的要求,譬如输出到控制台,输出到文件,同时输出到控制台跟文件等。每一种情况都实现一个函数显然不可行,为了应付这种情况,我们可以考虑如下数据结构:

class Print{};
class PrintToConsole : public Print{}; 

5.如何使用

(1)让所有代码都#include "DebugNew.h",如果有的代码包含而有的代码不包含,内存分配与释放信息也就记录不准确了。

(2)此代码无法处理多线程的情况

6.使用效果

#include "DebugNew.h"
#include 
<stdlib.h>
#include 
<iostream>
#include 
<conio.h>

using namespace std; 
    
int main(int argc , char* argv[])
{
    
new int;
    Check();
    _getch();
    
return 0;
}

#include "DebugNew.h"
#include 
<stdlib.h>
#include 
<iostream>
#include 
<conio.h>

using namespace std; 
    
int main(int argc , char* argv[])
{
    
int* p=new int;
    delete p;
    Check();
    _getch();
    
return 0;
}

DebugNew.h

 1#ifndef DEBUGNEW_H
 2#define DEBUGNEW_H
 3
 4#include <iostream>
 5#include <stdlib.h>
 6#include "Link.h"
 7#include "Print.h"
 8
 9using namespace std;
10
11class NewListNode
12{
13public:
14    void* Object; //内存分配后获得的指针
15    size_t Size; //内存分配的大小
16    char* FileName; //内存分配代码所在文件位置
17    int LineNum; //内存分配代码所在行号
18
19    NewListNode();
20    NewListNode(void* Buffer, size_t s, char* File, int Line);
21    NewListNode(const NewListNode& Temp);
22}
;
23
24class NewList
25{
26public:
27    Link<NewListNode> Data;
28
29    bool IsEmpty();
30    void Add(const NewListNode& Temp);
31    void Remove(void* Object);
32    void Check();
33}
;
34
35extern void* operator new(size_t Size, char* FileName, int LineNum);
36extern void* operator new[](size_t Size, char* FileName, int LineNum);
37extern void operator delete(void* Object);
38extern void operator delete[](void* Object);
39
40extern void Check();
41extern NewList NewRecord;
42
43#define new new(__FILE__, __LINE__ )
44#endif

 

DebugNew.cpp

  1#include "DebugNew.h"
  2#undef new
  3
  4NewListNode::NewListNode()
  5{
  6    Object=0;
  7    FileName=0;
  8}

  9
 10NewListNode::NewListNode(void* Buffer, size_t s, char* File, int Line)
 11{
 12    Object=Buffer;
 13    Size=s;
 14    FileName=File;
 15    LineNum=Line;
 16}

 17
 18NewListNode::NewListNode(const NewListNode& Temp)
 19{
 20    FileName=Temp.FileName;
 21    LineNum=Temp.LineNum;
 22    Object=Temp.Object;
 23    Size=Temp.Size;
 24}

 25
 26bool NewList::IsEmpty()
 27{
 28    if (Data.GetHead()) return false;
 29    else return true;
 30}

 31
 32void NewList::Add(const NewListNode& Temp)
 33{
 34    Data.AddLast()->Data=Temp;
 35}

 36
 37void NewList::Remove(void* Object)
 38{
 39    Node<NewListNode>* Temp=Data.GetHead();
 40    while (Temp)
 41    {
 42        if (Temp->Data.Object==Object)
 43        {
 44            Data.Delete(Temp);
 45            return;
 46        }

 47        Temp=Temp->Next;
 48    }

 49}

 50
 51void NewList::Check()
 52{
 53    PrintToConsole Printer;
 54    if (!IsEmpty())
 55    {
 56        Printer.Writeln("存在内存泄漏");
 57        Printer.Writeln("Line\tSize\tPath");
 58        Printer.WriteLine();
 59        Node<NewListNode>* Temp=Data.GetHead();
 60        while (Temp)
 61        {
 62            char Line[20];
 63            itoa(Temp->Data.LineNum, Line, 10);
 64            Printer.Write(Line);
 65            Printer.Write("\t");
 66            char s[200];
 67            itoa(Temp->Data.Size, s, 10);
 68            Printer.Write(s);
 69            Printer.Write("\t");
 70            Printer.Write(Temp->Data.FileName);
 71            Printer.Write("\t");
 72            Temp=Temp->Next;
 73        }

 74    }

 75    else Printer.Writeln("不存在内存泄漏");
 76}

 77
 78NewList NewRecord;
 79
 80void* operator new(size_t Size, char* FileName, int LineNum)
 81{
 82    if (Size==(size_t)0) Size=1;
 83    void* Result=::operator new(Size);
 84    if (!Result) throw bad_alloc();
 85    else
 86    {
 87        NewListNode Temp(Result, Size, FileName, LineNum);
 88        NewRecord.Add(Temp);
 89        return Result;
 90    }

 91}

 92
 93void* operator new[](size_t Size, char* FileName, int LineNum)
 94{
 95    if (Size==(size_t)0) Size=1;
 96    void* Result=::operator new[](Size);
 97    if (!Result) throw bad_alloc();
 98    else
 99    {
100        NewListNode Temp(Result, Size, FileName, LineNum);
101        NewRecord.Add(Temp);
102        return Result;
103    }

104}

105
106void operator delete(void* Object)
107{
108    if (!Object) return;
109    NewRecord.Remove(Object);
110    free(Object);
111}

112
113void operator delete[](void* Object)
114{
115    if (!Object) return;
116    NewRecord.Remove(Object); 
117    free(Object);
118}

119
120void Check()
121{
122    NewRecord.Check();
123}

 

Print.h

 1#ifndef PRINT_H
 2#define PRINT_H
 3
 4#include <stdlib.h>
 5#include <iostream>
 6
 7using namespace std;
 8
 9class Print
10{
11public:
12    virtual ~Print();    
13}
;
14
15class PrintToConsole : public Print
16{
17public:
18    virtual ~PrintToConsole();
19    virtual void Write(char* String);
20    virtual void Writeln(char* String);
21    virtual void WriteLine();
22}

23
24#endif

 

Print.cpp

 1#include "Print.h"
 2
 3Print:: ~Print()
 4{
 5}

 6
 7PrintToConsole::~PrintToConsole()
 8{
 9}

10
11void PrintToConsole::Write(char* String)
12{
13    if (String) cout<<String;
14}

15
16void PrintToConsole::Writeln(char* String)
17{
18    if (String) cout<<String<<endl;
19    else cout<<endl;
20}

21    
22void PrintToConsole::WriteLine()
23{
24    cout<<"-------------------------------------------------------------------------------"<<endl;
25}
posted on 2009-03-25 01:56 Lyt 阅读(3192) 评论(2)  编辑 收藏 引用 所属分类: 其他

FeedBack:
# re: 一个简单的内存泄漏检测器
2009-03-25 02:00 | 陈梓瀚(vczh)
1:你的delete用的是free,你要么用operator delete,要么用malloc分配内存。
2:PrintToConsole Printer;这样是不对的,别人没办法将他自己的Printer传给你。  回复  更多评论
  
# re: 一个简单的内存泄漏检测器
2009-03-27 19:20 | 路过
简单就用标准库直接带的就好了啊
#include <crtdbg.h>
#define DEBUG_NEW new(_NORMAL_BLOCK ,__FILE__, __LINE__)
  回复  更多评论
  

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理