-
程序存储区
-
静态存储区
-
动态存储区。
-
-
分配一个内存块并使用其中未经初始化的内容;
-
释放一个内存块,但继续引用其中的内容;
-
子函数中分配的内存空间在主函数出现异常中断时、或主函数对子函数返回的信息使用结束时,没有对分配的内存进行释放;
-
程序实现过程中分配的临时内存在程序结束时,没有释放临时内存。内存错误一般是不可再现的,开发人员不易在程序调试和测试阶段发现,即使花费了很多精力和时间,也无法彻底消除。
产生方式的分类
这意味着如果这个地址值(保存在x里)丢失了,就会发生内存泄漏问题。 地址值会因为很多原因而丢失哦,比如因为一个指针变量被无意中改写,例如:new *x; x = new int[1000]; delete[] x; x = NULL;
大家看出来了吗?这是会导致内存泄漏的情况之一。int *x; x = new int[3000]; x = new int[4000]; delete[] x; x = NULL;
当foo()函数结束时,指针变量x将超出它的作用域,这意味着它将不复存在,它的值当然就会丢失。void foo() { My Class *x; x = new MyClass(); }
有两种方法可以用来堵住这样的漏洞:
第一个方法是在return语句之前的某个地方插入一条delete x语句:第二个方法是让函数把内存块的地址返回给它的调用者:void foo() { MyClass *x; x = new MyClass(); delete x; x = NULL; return; }
内存作用域MyClass *foo() { MyClass *x; x = new MyClass(); return x; }
变量都有一个作用域:规定了它们可以在程序的哪些部分使用。 这个作用域通常就是对它们做出声明和定义的函数的函数体,如main函数或某个子函数。 如果被定义在任何一个函数的外部,变量将拥有全局作用域,这意味着它们可以在整个程序中的所有函数里使用。
2)程序运行过程中避免内存的分配操作;(如可以采取预先分配好内存缓冲区的方式)--这条其实很重要;
3)采取STL组件的动态数组代替手工分配对象内存的方式也是一种办法;
1. 用类指针(point-like)对象代替原始指针(raw point)
指针是C++的一种特性。使用指针能给我们带来巨大的方便,但如果使用不当,内存动态分配以后没有及时收回,那么系统的内存资源极有可能泄漏,直至造成程序崩溃。
例如:
class A {……}; //定义一个类型A
定义好一个对象后,我们一般可以用new方法,来创造一个对象实例。
A *pA = new A; //动态分配一个内存,用new方法,相应的就需要调用delete方法,将该内存进行回收.
然而,在大量的程序代码中,直接针对每一个new,使用一个delete的方式,存在以下几个问题:
1) 程序进行维护期间,开发人员若不慎增加循环、条件控制流语句,程序在未触及到delete时,就运行了return;
2) 在new和delete之间,由于运行了某些操作,导致出现异常。
因此,我们不能百分百保证,delete语句总是会执行。为了确保内存资源总是能够回收,我们可以将指针,用一个对象来进行操作,从而利用C++的“析构函数自动调用机制”自动释放内存资源。在标准的程序库中,提供了auto_ptr和tr1::shared_ptr两种指针对象,我们也可以称之为智能指针,采用这种指针对象,当对象的生命周期结束时,其析构函数将自动调用delete。其用法如下:
std::auto_ptr<A> pA(new A);
std::tr1::shared_ptr<A> pA(new A);
这两个指针对象的区别之处在于:多个auto_ptr不能同时指向同一对象;而多个shared_ptr则可以指向同一对象。正是因为这个不同,造成两者在处理copy构造函数和copy assignment操作符时,也不尽相同。auto_ptr的复行为具有特殊性:
std::auto_ptr<A> pA1(new A);
std::auto_ptr<A> pA2(pA1); //pA2指向对象,而pA1为空
pA1 = pA2; //pA1指向对象,而pA2为空
2. 成对使用new与delete时应采用相同的形式
一般来讲,new方法可以用于创建单对对象,也可以用于创建数组对象。同样,针对单个对象与数组,调用delete的形式也不尽相同。如下所示的一个例子,就是new与delete调用形式不匹配,从而造成内存没有成功释放。
string* sArray = new string[100];
……
delete sArray;
由于sArray是一个数组,因而上述的100个string对象,就必须调用100次析构函数才能完全释放内存,因而正确的做法是:
delete [] sArray;
因此,如果你在new表达式中使用[ ],必须在相应的delete表达式中也使用[ ]。如果你在new表达式中不使用[ ],那相应的delete表达式也不能使用[ ]。
3. 在独立的语句中构建智能指针
考虑到如下的函数:
void f(std::tr1::shared_ptr<A> pA(new A), fun());
编译器在调用f函数的具体内容前,首先要处理被传递的各个实参。上述第一个参由两部分构成:执行new A,然后调用std::tr1::shared_ptr构造函数。所在编译器在调用f之前,必须做三件事:
1. 调用函数fun
2. 执行new A
3. 调用tr1::shared_ptr构造函数
然后,对于C++来讲,编译器的执行顺序是不确定的,如果最终的操作顺序是这样:
1. 执行new A
2. 调用函数fun
3. 调用tr1::shared_ptr构造函数
如果是这样,假如在调用函数fun时,发现了异常,此时new A返回的指针将会遗失,而且没有放入tr1::shared_ptr中,所以就可能在调用这个函数时引发内存泄漏。为了避免这种问题,一般采用两条语句来实现上述代码,如下:
std::tr1::shared_ptr<A> pA(new A);
f(pA, fun());