c++内存管理

1.C++内存分配方式

堆:堆是操作系统中的术语,是操作系统所维护的一块特殊内存,用于程序的内存动态分配,C语言使用malloc从堆上分配内存,使用free释放已分配的对应内存。

栈:在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。

自由存储区:自由存储区是C++基于new操作符的一个抽象概念,凡是通过new操作符进行内存申请,该内存即为自由存储区。(那么自由存储区是否能够是堆(问题等价于new是否能在堆上动态分配内存),这取决于operator new 的实现细节。自由存储区不仅可以是堆,还可以是静态存储区,这都看operator new在哪里为对象分配内存。)

全局/静态存储区:这块内存是在程序编译的时候就已经分配好的,在程序整个运行期间都存在。例如全局变量,静态变量。

常量存储区:这是一块比较特殊的存储区,他们里面存放的是常量(const),不允许修改。

PS:网上多说人认为,自由存储区与堆的划分标准是申请和释放内存是使用的new/delete还是malloc/free。C++标准并没有给出new/delete应该如何实现,但很多编译器的new/delete都是以malloc/free为基础来实现的。从技术上来说,堆(heap)是C语言和操作系统的术语,堆是操作系统所维护的一块特殊内存,它提供了动态分配的功能,使用malloc()、free()来申请/释放内存。而自由存储是C++中通过new和delete动态分配和释放对象的抽象概念。基本上,所有的C++编译器默认使用堆来实现自由存储。也就是说,默认的全局运算符new和delete也许会使用malloc和free的方式申请和释放存储空间,也就是说自由存储区就位于堆上。但程序员也可以通过重载操作符,改用其他内存来实现自由存储,例如全局变量做的对象池,这时自由存储区就不位于堆上了。

2.C++ 内存分配与释放(与C的malloc/free对比)

特征new/deletemalloc/free
分配内存的位置 自由存储区
内存分配成功的返回值 完整类型指针 void*
内存分配失败的返回值 默认抛出异常 返回NULL
分配内存的大小 由编译器根据类型计算得出 必须显式指定字节数
处理数组 有处理数组的new版本new[] 需要用户计算数组的大小后进行内存分配
已分配内存的扩充 无法直观地处理 使用realloc简单完成
是否相互调用 可以,看具体的operator new/delete实现 不可调用new
分配内存时内存不足 客户能够指定处理函数或重新制定分配器 无法通过用户代码进行处理
函数重载 允许 不允许
构造函数与析构函数 调用 不调用

3.常见内存错误

      (1)内存分配未成功,却使用了它。

  编程新手常犯这种错误,因为他们没有意识到内存分配会不成功。常用解决办法是,在使用内存之前检查指针是否为NULL。如果指针p是函数的参数,那么在函数的入口处用assert(p!=NULL)进行检查。如果是用malloc来申请内存,应该用if(p==NULL) 或if(p!=NULL)进行防错处理。但是如果用new申请内存,则不能用刚刚的方法。因为new内存分配失败时,会抛出bac_alloc异常,它不会返回NULL;malloc分配内存失败时返回NULL。所以对于new来说,正确的做法是抛出异常。

try
{
    int *a = new int();
}
catch (bad_alloc)
{
    ...
}

 

      (2) 内存分配虽然成功,但是尚未初始化就引用它。

  犯这种错误主要有两个起因:一是没有初始化的观念;二是误以为内存的缺省初值全为零,导致引用初值错误(例如数组)。 内存的缺省初值究竟是什么并没有统一的标准,尽管有些时候为零值,我们宁可信其无不可信其有。所以无论用何种方式创建数组,都别忘了赋初值,即便是赋零值也不可省略,不要嫌麻烦。

      (3)内存分配成功并且已经初始化,但操作越过了内存的边界。

  例如在使用数组时经常发生下标“多1”或者“少1”的操作。特别是在for循环语句中,循环次数很容易搞错,导致数组操作越界。

     (4)忘记了释放内存,造成内存泄露。

  含有这种错误的函数每被调用一次就丢失一块内存。刚开始时系统的内存充足,你看不到错误。终有一次程序突然死掉,系统出现提示:内存耗尽。动态内存的申请与释放必须配对,程序中malloc与free的使用次数一定要相同,否则肯定有错误(new/delete同理)。

     (5)释放了内存却继续使用它。

  有三种情况:

  (a)程序中的对象调用关系过于复杂,实在难以搞清楚某个对象究竟是否已经释放了内存,此时应该重新设计数据结构,从根本上解决对象管理的混乱局面。

  (b)函数的return语句写错了,注意不要返回指向“栈内存”的“指针”或者“引用”,因为该内存在函数体结束时被自动销毁。

  (c)使用free或delete释放了内存后,没有将指针设置为NULL。导致产生“野指针”。

常见的内存错误对策如下:

      【规则1】用malloc或new申请内存之后,应该立即检查指针值是否为NULL。防止使用指针值为NULL的内存。

  【规则2】不要忘记为数组和动态内存赋初值。防止将未被初始化的内存作为右值使用。

  【规则3】避免数组或指针的下标越界,特别要当心发生“多1”或者“少1”操作。

  【规则4】动态内存的申请与释放必须配对,防止内存泄漏。

  【规则5】用free或delete释放了内存之后,立即将指针设置为NULL,防止产生“野指针”。

posted @ 2020-02-22 08:23  卖雨伞的小男孩  阅读(288)  评论(0编辑  收藏  举报