定义
简单来说,内存泄漏就是程序在申请一个内存空间后没有释放,直到程序运行结束后才释放。这样看起来似乎没什么大问题,但是如果程序会持续运行很长时间(例如服务器),并且可能在程每次调用某个部分的时候都会申请一个内存空间,那么长久以来的后果是可想而知的:当程序希望再次申请一块空间时,发现已经没有free的部分了,最终导致系统崩溃。
情况
内存泄漏可能发生在如下几种条件下:
1. 类的构造和析构函数不合理。 显式 --- 很容易想到的情况,使用new在堆里创建对象后,没有delete。 隐式 --- 在构造函数或者其他方法体内动态分配内存,但是在析构函数中没有释放 说明: 以上几种情况可以通过自定义构造和析构函数解决。
2. 嵌套的对象指针未清除
3. 指向对象的指针数组和对象数组的区别 对象数组 --- 一个数组中保存多个对象,注意释放时添加 [] 符号。 指向对象的指针数组 --- 仅仅释放数组是不够的,数组中的每个元素都指向一个对象,需要一次释放元素所指对象。 说明: 2 和 3 都是定义未明确时容易出现的错误。
4. 同一个内存地址被释放两次 缺少拷贝构造函数 --- 如果类成员有指针,那么在C++中的默认拷贝构造函数同时也会复制指针,从而导致有两个指针指向同一个地址,那么在释放拷贝者对象与被拷贝者对象的时候,同一个地址会被释放两次,这是不允许的。需要程序员自己添加拷贝构造函数,防止这种问题的出现。 缺少赋值运算符重载函数 --- 和前面一种情况类似,只不过赋值运算符重载函数调用的时机与拷贝构造函数不同,解决方法也是程序员自己添加赋值运算符重载函数。 说明: 如果一个程序语句生成了一个新的对象实例,那么它就会调用拷贝构造函数;如果这条语句是赋值语句(例如 B = A;)而非初始化语句(例如 ClassB B = A;),那么它还会调用赋值运算符重载函数。注意,这两者是有可能在同一个语句中调用的(例如 B = f1(A);)。
5. 基类指针指向子类对象时(例如 Parent *p = son;),如果基类的析构函数没有声明为虚函数,那么在直接释放指针 p 时,不会调用 Class Son {} 中的析构函数,也就导致派生类对象 son 没有释放。