zoukankan      html  css  js  c++  java
  • 从零开始学C++之重载 operator new 和 operator delete 实现一个简单内存泄漏跟踪器

    先来说下实现思路:可以实现一个Trace类,调用 operator new 的时候就将指向分配内存的指针、当前文件、当前行等信息添加进Trace 成员map容器内,在调用operator delete 的时候删除这些信息。定义一个全局Trace 对象,当程序结束,对象析构时判断成员map 是否还有信息,如果有则打印出来,表示已经发生内存泄漏,从输出可以看出是哪一个文件哪一行分配了内存但没有释放掉。


    DebugNew.h:

     C++ Code 
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    #ifndef _DEBUG_NEW_H_
    #define _DEBUG_NEW_H_

    #ifndef NDEBUG
    #include  "Tracer.h"
    #define  new  new(__FILE__, __LINE__)
    #endif  // NDEBUG

    #endif  // _DEBUG_NEW_H_

    Trace.h:

     C++ Code 
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
     
    #ifndef _TRACER_H_
    #define _TRACER_H_

    #include <map>

    #ifndef NDEBUG

    void * operator  new(size_t size,  const  char *file,  long line);
    void  operator  delete( void *p);

    void * operator  new[](size_t size,  const  char *file,  long line);
    void  operator  delete[]( void *p);

    class Tracer
    {
    private:
         class Entry
        {
         public:
            Entry( const  char *file =  0long line =  0)
                : file_(file), line_(line) {}
             const  char *File()  const
            {
                 return file_;
            }
             long Line()  const
            {
                 return line_;
            }
         private:
             const  char *file_;
             long line_;
        };
    public:
        Tracer();
        ~Tracer();
         static  bool Ready;

         void Add( void *p,  const  char *file,  long line);
         void Remove( void *p);
         void Dump();

    private:
        std::map< void *, Entry> mapEntry_;
    };

    #endif  // NDEBUG

    #endif  // _TRACER_H_

    Trace.cpp:


     C++ Code 
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
     
    #include <iostream>
    #include  "Tracer.h"

    #ifndef NDEBUG

    bool Tracer::Ready =  false;

    Tracer::Tracer()
    {
        Ready =  true;
    }

    Tracer::~Tracer()
    {
        Ready =  false;
        Dump();
    }

    void Tracer::Add( void *p,  const  char *file,  long line)
    {
        mapEntry_[p] = Entry(file, line);
    }

    void Tracer::Remove( void *p)
    {
        std::map< void *, Entry>::iterator it;
        it = mapEntry_.find(p);
         if (it != mapEntry_.end())
        {
            mapEntry_.erase(it);
        }
    }

    void Tracer::Dump()
    {
         if (mapEntry_.size() >  0)
        {
            std::cout <<  "*** Memory leak(s):" << std::endl;
            std::map< void *, Entry>::iterator it;

             for (it = mapEntry_.begin(); it != mapEntry_.end(); ++it)
            {
                 const  char *file = it->second.File();
                 long line = it->second.Line();
                 int addr =  reinterpret_cast< int>(it->first);
                std::cout <<  "0x" << std::hex << addr <<  ": "
                          << file <<  ", line " << std::dec << line << std::endl;

            }
            std::cout << std::endl;
        }
    }

    Tracer NewTrace;

    void * operator  new(size_t size,  const  char *file,  long line)
    {
         void *p = malloc(size);
         if (Tracer::Ready)
        {
            NewTrace.Add(p, file, line);
        }
         return p;
    }


    void  operator  delete( void *p)
    {
         if (Tracer::Ready)
        {
            NewTrace.Remove(p);
        }
        free(p);
    }

    void * operator  new[](size_t size,  const  char *file,  long line)
    {
         void *p = malloc(size);
         if (Tracer::Ready)
        {
            NewTrace.Add(p, file, line);
        }
         return p;
    }

    void  operator  delete[]( void *p)
    {
         if (Tracer::Ready)
        {
            NewTrace.Remove(p);
        }
        free(p);
    }
    #endif  // #ifndef NDEBUG

    main.cpp:

     C++ Code 
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
     
    #include <iostream>
    using  namespace std;

    #include  "DebugNew.h"

    int main( void)
    {
         int *p =  new  int;
         /*delete p;*/

         int *p2 =  new  int[ 5];
         /*delete[] p2;*/

         return  0;
    }



    程序 #define new new(__FILE__, __LINE__); 是为了利用__FILE__, 和 __LINE__两个宏,分别代表文件名和行数。分别重载了


    operator new 和 operator new[]  函数以及对应的delete,更详细的讨论可以参见这里。当全局对象NewTrace 析构时调用Dump成员


    函数,如果new 和 delete 没有匹配,那么map将存在泄漏信息,并打印出来。


    此外只在Debug版本(没有定义NDEBUG)才跟踪内存泄漏,所以加上#ifndef NDEBUG ... #endif 


    而由于一般的C++库中可能没有#define new new(__FILE__, __LINE__);  即调用的还是原始的new,但现在程序中并没有重载这种类


    型的new和delete函数,故并不能跟踪类似map容器之类的内存泄漏,但一般正常使用C++库容器的话,是不会造成内存泄漏的,


    C++库已经实现得比较完善了,至少比我们自己写的程序要好很多。


    参考:

    C++ primer 第四版
    Effective C++ 3rd
    C++编程规范


  • 相关阅读:
    ubuntu上搭建review board代码评审站点
    android5.1 for tq335x
    2015.04.11
    DECLARE_GLOBAL_DATA_PTR宏定义问题
    6410移植android4.4.2笔记(持续更新)
    ubuntu安装packet提示重复冲突问题
    android5.0 aosp编译记录(由于机器硬件原因,改为4.4.2编译通过)
    2015.01.16
    2015.01.14
    SDSM框架
  • 原文地址:https://www.cnblogs.com/jiangu66/p/3199034.html
Copyright © 2011-2022 走看看