一、什么是内存泄漏:
总结就是new出来的内存没有通过delete合理的释放掉。
用动态存储分配函数动态开辟的空间,在使用完毕后未释放,结果导致一直占据该内存单元,不能被任何程序再次使用,直到程序结束。即所谓内存泄漏。
注意:内存泄漏是堆内存的泄漏;
简单的说就是申请了一块内存空间,使用完毕后没有释放掉。它的一般表现方式是程序运行时间越长,占用内存越多,最终用尽全部内存,整个系统崩溃。由程序申请的一块内存,且没有任何一个指针指向它,那么这块内存就泄露了。
二、造成内存泄漏的原因
1、在类的构造函数和析构函数中没有匹配的使用new和delete关键字;
2、没有正确清除嵌套的对象指针;
3、在释放对象数组时在delete中未使用方括号[],正确是delete [] p;
4、指向对象的数组指针不等同于对象数组;
对象数组是指:数组中存放的是对象,只需要delete []p,即可调用对对象数组中的每个对象的析构函数释放空间。
指向对象的指针数组是指:数组中存放的是指向对象的指针,不仅要释放每个对象的空间,还要释放每个指针的空间,delete []p只是释放了每个指针,但是并没有释放对象的空间,正确的做法是通过一个循环,将每个对象释放后再将指针释放。
5、缺少拷贝构造函数:两次释放相同的内存是一种错误的做法,同时可能造成堆的崩溃。按值传递会调用拷贝构造函数,而引用传递不会。因此如果一个类里面有指针成员变量,要么必须显式的写拷贝构造函数和重载赋值运算符,要么禁用拷贝构造函数和重载赋值运算符。
6、缺少重载赋值运算符:
7、关于nonmodifying运算符重载的常见迷思
a.返回栈上对象的引用或指针,导致最后返回的是一个空引用或空指针,因此变成野指针
b.返回内部静态对象的引用
c.返回一个泄漏内存的动态分配的对象,导致内存泄漏,并且无法回收
解决这一类问题的办法是重载运算符函数的返回值不是类型的引用,而是类型的返回值,即应该是int而不是int &。
8、没有将基类的析构函数定义为虚函数
当基类指针指向子类对象时,如果基类的析构函数不是virtual的,那么子类的析构函数不会被调用,子类对象的资源没有正确被释放,因此会造成内存泄漏。
三、什么是野指针
野指针不是NULL指针,是指未初始化或未清零的指针,他指向的内存不是程序员想要的内存;野指针是非常危险的;
野指针的成因主要是三个:
一是指针变量未被初始化;任何指针变量在被创建时,不会主动被赋值为NULL指针,他的缺省值是随机的,乱指一气;所以指针变量在被创建时应当被初始化,要么初始化为NULL指针,要么让他指向一个合法的内存地址;
二是指针被free或者delete之后,没有置为NULL,让人误以为是一个合法的指针;free或delete只是将指针p指向的内存释放掉,但p指针本身并没有被干掉。通常会用if(p!=NULL)进行防错处理。但这里if语句并不起作用,因为p不是NUll指针,也不指向任何合法的地址;
1 #include<stdio.h>
2 #include<stdlib.h>
3
4 int main()
5 {
6 char *p=(char *)malloc(sizeof(char)*100);
7 strcpy(p, “hello”);
8 printf(“%s ”,p);
9 free(p); // p 所指的内存被释放,但是p所指的地址仍然不变
10 if(p != NULL) // 没有起到防错作用
11 strcpy(p, “world”); // 出错
12 printf(“%s \n”,p);
13 }
free()释放的是指针指向的内存!注意!释放的是内存,不是指针!这点非常非常重要!指针是一个变量,只有程序结束时才被销毁。释放了内存空间后,原来指向这块空间的指针还是存在!只不过现在指针指向的内容的垃圾,是未定义的,所以说是垃圾。因此,前面我已经说过了,释放内存后把指针指向NULL,防止指针在后面不小心又被解引用了。非常重要啊这一点!
三是指针操作超过了变量的作用范围,比如返回指向栈内存的指针就是野指针。
在使用指针的时候还要注意的问题:
1:不要返回指向栈内存的指针或引用,因为栈内存在函数结束时会被释放.
2: 在使用指针进行内存操作前记得要先给指针分配一个动态内存。