在cpp编程中,产生内存泄漏的主要原因是利用malloc或者new等分配内存的方式申请内存后,由于主观或者客观原因没有进行释放,导致申请的内存区域没有及时得到释放导致的。

下面对几种常见/特殊的情况进行简单记录。

1. 析构函数中未匹配地释放内存

2. 基类的析构函数没有设为虚函数,

由于基类的析构函数不是虚函数,在实现多态时,通过delete删除指向派生类的基类指针时,派生类的析构函数被覆盖无法调用,而是调用的基类的析构函数,导致派生类对象无法被释放,导致内存泄漏。

3. 关于指针数组释放

对于二维数组,利用指针的定义区别如下:

int (*a)[N]=new int[M][N];  //N必须为已知
//对应内存释放为
delete[] a;


int **b=new int*[M];
for(int i=0;i<M;i++)
{
        b[i]=new int[N];  
}
//对应内存释放为

for(int i=0;i<M;i++)
{
        delete[] b[j];      
}

delete[] b;

delete[]不光需要释放对象的空间,还要释放其中的每个指针,否则释放的不彻底。

4. 拷贝构造函数和运算符重载的问题

当类缺少拷贝构造函数时,可能造成内存泄露问题。具体场景主要是当一个类中有指针成员,但是没有编写拷贝构造函数时,系统将会调用默认构造函数,采用值传递的方式。这种隐式传递的方式容易造成两个对象同时具有指向同一个地址的指针成员。因此在释放对象的时候,第一个对象能够正常释放,而第二个对象的释放将会释放相同的内存,这是一种错误的做法,可能会导致堆的崩溃。因此当类中含有指针成员时,应当显示地重写构造函数和重载运算符,以保证深拷贝的发生。

5. shared_ptr的循环引用问题

身为智能指针的shared_ptr也可能存在内存泄露问题。如下面情况

当类A和类B中分别含有类型为对方的智能指针,并且在各自创建实例a和b后,对实例a/b中的智能指针传入b/a进行了赋值。那么在离开作用域后,实例a和实例b中的智能指针引用计数都是1,但是都在等待对方释放后才能释放,这种情况便造成了循环引用的问题。具体代码实例如下:

class ClassA {
public:
  ...
    void setInnerPtr(shared_ptr<ClassB> pB) {
        p = pB;
    }
private:
    shared_ptr<ClassB> p;
};

class ClassB {
public:
  ...
    void setInnerPtr(shared_ptr<ClassA> pA) {
        p = pA;
    }

private:
    shared_ptr<ClassA> p;
};
int main()
{
  shared_ptr<ClassA> pA = make_shared<ClassA>();
  shared_ptr<ClassB> pB = make_shared<ClassB>();

  pA->setInnerPtr(pB);
  pB->setInnerPtr(pA);
  ...
}

这种情况可以引入weak_ptr来解决,在ClassA和ClassB的声明中用weak_ptr代替shared_ptr后,不会产生引用计数,因此不会产生循环引用的问题。

其他会造成野指针的情况:

1)指针变量没有被初始化(如果值不定,可以初始化为NULL)

2)指针所指内存被释放后,没有置为NULL。

3)指针操作超越了变量的作用范围,比如返回指向栈内存的指针就是野指针。