andalousie 发表于 2014-2-23 14:35:26

跟踪内存泄漏

本帖最后由 andalousie 于 2014-2-25 08:41 编辑

我在书上看到可以通过重载new和delete运算符实现跟踪内存泄漏。这里把我自己的例子发布出来。
用于测试的文件test.cpp
#include <iostream>
#include <cstdlib>
#if !defined NDEBUG
#include "Tracer.h"
#define new new (__FILE__, __LINE__)
Tracer NewTrace;
#endif
#include <cstring>
/************************************************
* info -- A class to hold information.         *
*                                              *
* Note:                                        *
*      Because someone is walking all over our *
*      memory and destroying our data, we      *
*      have put two guards at the beginning    *
*      and end of our class.    If someone   *
*      messes with us these numbers will       *
*      be destroyed.                           *
*                                              *
* Member functions:                            *
*      set_data -- Store a string in our data. *
*      get_data -- Get the data string. *
*      check_magic -- Check the magic numbers. *
************************************************/
// Magic numbers for the start and end of the
// data in the class info
const int START_MAGIC = 0x11223344;
const int END_MAGIC = 0x5567788;
class info
{
    private:
      // Magic protection constant
      const int start_magic;
      // String to be stored
      char data;
      // Magic protection constant
      const int end_magic;
    public:
      info(void):
            start_magic(START_MAGIC),
            end_magic(END_MAGIC)
            {}
      // Copy constructor defaults
      // Assignment operator defaults
      // Destructor defaults
      // Store some data in the class
      void set_data(
            // Data to be stored
            const char what[]
      )
      {
            strcpy(data, what);
      }
      // Get the data from the class
      char *get_data(void)
      {
            return (data);
      }
      // Verify that the magic
      // numbers are correct
      void check_magic(void)
      {
            if ((start_magic != START_MAGIC) ||
                (end_magic != END_MAGIC))
            {
                std::cout <<
                  "Info has lost its magic\n";
            }
      }
};
/************************************************
* new_info -- Create a new version of the      *
*      info class.                           *
************************************************/
struct info *new_info(void)
{
    struct info *result; // Newly created result.
/*    result = (struct info *)
      malloc(sizeof(struct info));
    // Make sure the structure is clear
    memset(result, '\0',sizeof(result));*/
    result = new info;// Use C++ new rather than C malloc
    return (result);
}
int main()
{
    // An info class to play with
    class info *a_info = new_info();
    a_info->set_data("Data");
    a_info->check_magic();
    return (0);
}
测试结果
*** Memory leak(s)
0x11223344: ptr/main.cpp, line 82通过测试发现,在代码的第82行,也就是红色字体的,内存没有被释放。
这个代码本来是一个教学代码,如果使用绿色字体部分,而不是红色字体部分,就会输出
Info has lost its magic()
原因很简单,我们使用C语言形式的malloc分配空间不会调用构造函数,然后雪上加霜地用memset来将类置0……
对于这种问题,我只能给出一个我认为还算过得去的方案,就是使用智能指针。
我们可以自己写一个auto_ptr类,来用析构函数实现其内容的销毁。
#if !defined (AUTOPTR_H)
#define AUTOPTR_H

//-------------
// Auto pointer
//-------------
template<class T>
class auto_ptr
{
      template<class U> struct auto_ptr_ref
      {
                auto_ptr<U> & _ap;
                auto_ptr_ref (auto_ptr<U> & p) : _ap (p) {}
      };
public:
      typedef T element_type;

      explicit auto_ptr (T * p = 0) : _p (p) {}

      // "transfer" constructor
      auto_ptr (auto_ptr & pSrc)
      {
                _p = pSrc._p;
                pSrc._p = 0;
      }

      auto_ptr & operator= (auto_ptr & pSrc)
      {
                reset (pSrc.release ());
                return *this;
      }
#if 0 // not in this version of the compiler
      // "transfer" from derived class
      template<class U>
      auto_ptr (auto_ptr<U> & pSrc)
      {
                _p = pSrc._p; // implicit cast
                pSrc._p = 0;
      }

      // assignment of derived class
      template<class U>
      auto_ptr & operator= (auto_ptr<U> & pSrc)
      {
                if (this != &pSrc)
                {
                        _p = pSrc._p;
                        pSrc._p = 0;
                }
                return *this;
      }
#endif
      ~auto_ptr () { delete _p; }

      T & operator* () const { return *_p; }

      T * operator-> () const { return _p; }

      T * get () const { return _p; }

      T * release ()
      {
                T * tmp = _p;
                _p = 0;
                return tmp;
      }

      void reset (T * p = 0)
      {
                if (p != _p)
                {
                        delete _p;
                        _p = p;
                }
      }

      auto_ptr (auto_ptr_ref<T> apr)
                : _p (apr._ap.release ())
      {}

      template<class U>
      operator auto_ptr_ref<U> ()
      {
                return auto_ptr_ref<U> (*this);
      }

      template<class U>
      operator auto_ptr<U> ()
      {
                return auto_ptr<U> (release ());
      }
private:
      T * _p;
};

#endif而把结构体的内容改成
struct info *new_info(void)
{
    auto_ptr<info> result(new info);
    return (result.release());
}在return之后就会自动调用auto_ptr类的析构函数。至于形参new info,其实在使用以后系统就会将其释放,所以我认为没有影响。或者大家以可以使用<memory>头文件里面的std::auto_ptr,这样就用不着自己写了。
如果有其他好的解决方案,请回复。

凉石 发表于 2014-2-25 00:35:10

难度好大 啊
页: [1]
查看完整版本: 跟踪内存泄漏