如何阻止此指针内存泄漏?

时间:2017-10-30 19:22:24

标签: c++ pointers memory-leaks

我在c ++方面很不错,但在指针和内存方面,我总是很糟糕。我已经陷入了这种情况,我不知道是否有解决方案。

typedef unsigned long long ullong;

class MathClass { //This is just an example class
public:
    MathClass() {num = new ullong[1]();}

    MathClass operator+(MathClass b) { //This is not my actual function, just one that has the same problem
        MathClass c;
        c.num[0] = num[0] + b.num[0];
        delete [] num;
        num = NULL;
        return c;
    }
public:
    ullong* num;
};

这适用于这种情况。

MathClass a;
MathClass b;
for (int i = 0; i < 1000; i++) {
    a = a + b;
}

因为我设置的等于a + b,所以当+函数运行时,它会设置等于c并删除旧的数字。

对于这种情况,它会导致错误,因为我删除了b&#39;

MathClass a;
MathClass b;
MathClass c;
for (int i = 0; i < 1000; i++) {
    a = b + c;
}

如果我没有删除num,这将起作用,但这会导致内存泄漏。当我不删除num时,这个内存容易超过100MB。我确定答案很简单,但我无法弄清楚。

2 个答案:

答案 0 :(得分:0)

你需要三(五)的规则。

当你分配内存,或者你需要实现赋值,析构函数或复制构造函数时,你需要全部三个(五个 - 移动构造函数和移动赋值)。

当您使用new分配内存时(请考虑shared_ptr,unique_ptr),您需要控制复制分配和删除的工作方式。

class MathClass { //This is just an example class
public:
    MathClass() {num = new ullong[1]();}
    ~MathClass() { delete [] num;} // cleans up memory.
    MathClass( const MathClass & rhs ) { num = new ullong[1](); num[0] = rhs.num[0]; }
    MathClass& operator=( const MathClass & rhs )
    {
       if( &rhs != this ) {
          num[0] = rhs.num[0];
       }
       return *this;
    }

    MathClass operator+(MathClass b) { //This is not my actual function, just one that has the same problem
    MathClass c;
    c.num[0] = num[0] + b.num[0];
    // the wrong place delete [] num;
    num = NULL;
    return c;
    }
public:
    ullong* num;
};

operator +中的delete []位于错误的位置,因为它试图找到释放内存的正确位置。但是,最简单的方法就是应用rule of 3并确保内存在构造时构造,删除时删除,并且赋值(和移动)运算符正常工作

答案 1 :(得分:0)

问题实际上并不完全在指针中,而是运算符重载和类实现(如注释中所述)。 如果你在第一个例子中使用了争论者(即a = a + b - > a = b + a),你会看到与第二个例子中相同的错误。所以这里有关于实施operator overloading的好文章 代码看起来像这样

#include <iostream>
#include <algorithm>

typedef unsigned long long ullong;

class MathClass {

public:
    MathClass() { num = new ullong[ 1 ](); }
    MathClass( const MathClass &a ) { 
        *this = a;
    }
    ~MathClass() {  
        delete[] num;
        num = NULL;
    }

    MathClass &operator=( const MathClass &a ) {
        if ( this != &a ) {
            num = NULL;
            num = new ullong[ 1 ];
        }
        std::copy( a.num, a.num + 1, num );

        return *this;
    }

    friend MathClass operator+( const MathClass &a, const MathClass b ) {
        MathClass c;
        c.num[ 0 ] = a.num[ 0 ] + b.num[ 0 ];

        return c;

    }
ullong *num;
};

int main( int argc, char **argv ) {
   MathClass a;
   MathClass b;
   MathClass c;
   for ( int i = 0; i < 1000; ++i ) {
      std::cout << "a.num[ 0 ] is " << a.num[ 0 ] << std::endl;
      std::cout << "b.num[ 0 ] is " << b.num[ 0 ] << std::endl;
      a = b + a;
      a = c + b;
   }

   return 0;
}

在主要&#34; std :: cout&#34;仅用于输出可见性。当然我的复制构造函数的实现要好得多,但这里的关键点是当你使用coplex类型时,你几乎总是需要重新实现复制和赋值操作符,因为它们经常(如果不是总是)被调用。 还有一些其他时刻可以提及您的代码。至少自C ++ 11 NULL更改为nullptr以来,typedef也不常见。使用引用而不是指针,这是一个很好的做法,特别是在复杂参数的情况下,通过引用传递它们,而不是通过值,因为在传递值的情况下,将调用复制构造函数,并且您将必须具有相同的参数的实例,这可以导致记忆过度使用。