• 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 = 0, long 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++编程规范

  • 相关阅读:
    C++ 引用做左值
    C++ 引用本质的详解
    C++ 引用基础
    C语言错误 指针的类型错误
    C++ c++与C语言的区别(三目运算符,const修饰符)
    C++ c++与C语言的区别(struct类型的加强,函数-变量类型加强,bool类型)
    C++ c++与C语言的区别(实用性增强,register关键字增强,全局变量检测增强)
    C++ c++初识
    C语言 Linux内核链表(企业级链表)
    C语言 结构体中属性的偏移量计算
  • 原文地址:https://www.cnblogs.com/alantu2018/p/8471138.html
Copyright © 2020-2023  润新知