一、new和delete基本用法

程序开发中内存的动态分配与管理永远是一个让C++开发者头痛的问题,在C中,一般是通过malloc和free来进行内存分配和回收的,在C++中,new和delete已经完全包含malloc和free的功能,并且更强大、方便、安全。

new一般用法:

 new 类型 (初值)

用new分配数组空间时不能指定初值。

delete一般用法:

    delete [] 指针变量

[]部分是可选的,当释放数组所占内存时必须加[]。当你对一个指针使用 delete,delete 知道是否有数组大小信息的唯一方法就是由你来告诉它。如果你在你使用的 delete 中加入了方括号,delete 就假设那个指针指向的是一个数组。否则,就假设指向一个单一的对象。

    int *i = new int;             //没有初始值int *j = new int(100);         //初始值为100int *iArr = new int[3];        //分配具有3个元素的数组delete i;                   //释放单个变量所占用的内存
    delete j;delete []iArr;               //释放数组所占用的内存

一般我们常说的内存泄漏是指堆内存的泄漏。堆内存是指程序从堆中分配的,大小任意的(内存块的大小可以在程序运行期决定),使用完后必须显示释放的内存。应用程序一般使用malloc,realloc,new等函数从堆中分配到一块内存,使用完后,程序必须负责相应的调用free或delete释放该内存块,否则,这块内存就不能被再次使用,我们就说这块内存泄漏了。

二、new/delete和malloc/free的区别

对于非内部数据类型的对象而言,光用maloc/free无法满足动态对象的要求。对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。
由于malloc/free是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/free。
C++语言需要一个能完成动态内存分配和初始化工作的运算符new,以及一个能完成清理与释放内存工作的运算符delete。new出来的指针是直接带类型信息的。而malloc返回的都是void指针。

例1:

#include <iostream>
#include <malloc.h>
using namespace std;
class myclass
{
public:myclass(){i = 1;}void myfoo(){cout << "i = " << i << endl;}
private:int i;
};
int main()
{myclass *p = new myclass;myclass *q = (myclass *)malloc(sizeof(myclass));p->myfoo();q->myfoo();delete p;free(q);return 0;
}

程序执行结果为:
    i = 1
    i = 0

从上例可看出,new调用了类myclass的构造函数,而malloc只是分配了空间,并没有调用构造函数,因此会出现调用q->myfoo()函数时,输出的结果具有随机性。如果用free释放“new创建的动态对象”,那么该对象因无法执行析构函数而可能导致程序出错。如果用delete释放“malloc申请的动态内存”,理论上讲程序不会出错,但是该程序的可读性很差。所以new/delete必须配对使用,malloc/free也一样。

例2:

#include <iostream>
using namespace std;
class myclass
{
public:~myclass(){cout << "Goodbye" << endl;}
};
int main()
{myclass *p = new myclass;free(p);return 0;
}

上例中,~myclass()为类的析构函数,对象离开作用域或被delete的时候会调用。指针p指向了一个堆上创建的myclass对象,若用free来释放内存,则不会调用析构函数,所以上面的程序没有输出。如将free(p)改为:

delete p

程序执行时将会调用到myclass类的析构函数,输出结果为:
        Goodbye

三、new和多维数组

当使用new运算符定义一个多维数组变量或数组对象时,它产生一个指向数组第一个元素的指针,返回的类型保持了除最左边维数外的所有维数。例如:

  int *p1 = new int[10];

返回的是一个指向int的指针int*。

 int (*p2)[10] = new int[3][10]; 

new了一个二维数组,去掉最左边那一维[3],剩下int[10],所以返回的是一个指向int[10]这种一维数组的指针int (*)[10]。

  int (*p3)[3][10] = new int[5][3][10]; 

new了一个三维数组, 去掉最左边那一维[5], 还有int[3][10],所以返回的是一个指向二维数组int[2][10]这种类型的指针int (*)[3][10]。

四、内存分配时的出错处理

我们都知道,使用 malloc/calloc 等分配内存的函数时,一定要检查其返回值是否为“空指针”,即检查分配内存的操作是否成功,这是良好的编程习惯,也是编写可靠程序所必需的。但是,如果你简单地把这一招应用到 new 上,那可就不一定正确了。

例如:

int* p = new int[SIZE];
if ( p = = 0 ) // 检查 p 是否空指针return -1;

其实,这里的 if( p == 0 )完全是没意义的。在C++里,如果 new 分配内存失败,默认是抛出异常的。所以,如果分配成功,p == 0 就绝对不会成立;而如果分配失败了,也不会执行 if ( p == 0 ),因为分配失败时,new 就会抛出异常跳过后面的代码。如果你想检查 new 是否成功,应该捕捉异常:

try
{int* p = new int[SIZE];
}
catch ( const bad_alloc& e )
{return -1;
}

事实上,C++中并非只有抛出异常的new,也有不抛异常的new,即通常所说的“nothrow new”。可以这样使用它:

        T* p = new (nothrow) T(MAX_SIZE);

其中,nothrow是头文件<new>中定义的一个类型为std::nothrow_t的常量,我们可以直接使用它。这时,如果内存分配失败,p的值将为空(0),且不会有异常抛出,跟C的malloc很像了。

四、内存分配的“栈”和“堆”

栈,就是那些由编译器在需要的时候分配,在不需要的时候自动清除的变量的存储区。里面的变量通常是局部变量、函数参数等。
堆,就是那些由new分配的内存块,他们的释放编译器不去管,由我们的应用程序去控制,一般一个new就要对应一个delete。如果程序员没有释放掉,那么在程序结束后,操作系统会自动回收。

下面通过汇编代码来了解下栈和堆内存的分配:

int main()
{   int* p = new int;return 0;
}

其中,“int* p = new int;
”对应汇编代码为:

0041358E  push        4   //分配一个int型数据大小内存(4个字节),相当于call operator new前,参数入栈
00413590  call        operator new (4111D6h)
00413595  add         esp,4  //call operator new后,恢复栈结构
00413598  mov         dword ptr [ebp-0D4h],eax  //eax值给call operator   new返回的结果生成一个临时变量
0041359E  mov         eax,dword ptr [ebp-0D4h]  //临时变量的值赋给寄存器eax
004135A4  mov         dword ptr [p],eax   //寄存器eax值赋给栈上指针p

上面这句代码就涉及了内存分配的堆和栈,new分配了一块堆内存,指针p分配的是一块栈内存,这句代码的意思就是:在栈内存中存放了一个指向一块堆内存的指针p。在程序会先确定在堆中分配内存的大小,然后调用operator new分配内存,调用结束后返回值存入eax中,再将内存的首地址放入栈中(为p赋值)。

堆和栈主要的区别有以下几点:
(1)管理方式和碎片问题
    对于栈来讲,是由编译器自动管理,无需我们手工控制;对于堆来说,释放工作由程序员控制,容易产生内存碎片。
(2)分配效率
    栈是系统提供的数据结构,计算机会在底层对栈提供支持,分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。堆则是C/C++提供的,它的机制相对复杂,显然堆的效率比栈要低得多。

下面通过汇编代码分析下栈和堆内存存取的效率。

void main()
{char a = 1;char c[] = "1234567890";char* p = (char *)malloc(10);strcpy(p, c);a = c[1];a = p[1];return;
}

程序中对堆和栈存取的汇编代码为:

a = c[1];
004135E7  mov         al,byte ptr [ebp-1Fh]
004135EA  mov         byte ptr [ebp-9],al
a = p[1];
004135ED  mov         eax,dword ptr [ebp-2Ch]
004135F0  mov         cl,byte ptr [eax+1]
004135F3  mov         byte ptr [ebp-9],cl

可以看出,在栈上存取时直接就把字符串中的元素读到寄存器al中,在堆上存取时则要先把指针值读到eax中,在根据eax读取字符,显然慢了。

(3)增长方向不同
    栈内存由一个栈指针esp来开辟和回收,栈内存是从高地址向低地址增长的,增长时,栈指针向低地址方向移动,指针的地址值也就相应的减小;回收时,栈指针向高地址方向移动,地址值也就增加。所以栈内存的开辟和回收都只是指针的加减。
    对于堆来讲,增长方向是向上的,也就是向着内存高地址方向移动;回收时,指针向低地址方向移动,地址值也就减小。
(4)空间大小不同
    一般来讲在32位系统下,堆内存可以达到4G的空间,从这个角度来看堆内存几乎是没有什么限制的。但是对于栈来讲,一般都是有一定的空间大小的。无论是堆还是栈,都要防止越界现象的发生,因为越界的结果要么是程序崩溃,要么是摧毁程序的堆、栈结构,产生以想不到的结果。

转载于:https://www.cnblogs.com/wkfvawl/p/10701941.html

C++ new和delete 堆和栈相关推荐

  1. C/C++ 全局变量和局部变量在内存里的区别?堆和栈

    一.预备知识-程序的内存分配 一个由c/C++编译的程序占用的内存分为以下几个部分  1.栈区(stack)- 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等.其操作方式类似于数据结构中的栈 ...

  2. 堆和栈的区别(面试经验总结)

    C++中,内存分为5个区:堆.栈.自由存储区.全局/静态存储区和常量存储区. 栈:是由编译器在需要时自动分配,不需要时自动清除的变量存储区.通常存放局部变量.函数参数等. 堆:是由new分配的内存块, ...

  3. 堆和栈的差别(转过无数次的文章)

    一.预备知识-程序的内存分配    一个由C/C++编译的程序占用的内存分为下面几个部分    1.栈区(stack)-   由编译器自己主动分配释放   ,存放函数的參数值,局部变量的值等.其    ...

  4. 堆和栈的主要区别由以下几点:

    1.管理方式不同: 2.空间大小不同: 3.能否产生碎片不同: 4.生长方向不同: 5.分配方式不同: 6.分配效率不同: 管理方式:对于栈来讲,是由编译器自动管理,无需我们手工控制:对于堆来说,释放 ...

  5. 堆、栈、自由存储区、全局/静态存储区、常量存储区比较

    1.C语言中分为下面几个存储区 栈(stack): 由编译器自动分配释放 堆(heap): 一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收 全局区(静态区): 全局变量和静态变量的存储 ...

  6. 堆,栈,内存泄露,内存溢出介绍

    简单的可以理解为: heap(堆):是由malloc之类函数分配的空间所在地.地址是由低向高增长的. stack(栈):是自动分配变量,以及函数调用的时候所使用的一些空间.地址是由高向低减少的. 一. ...

  7. C++__堆,栈与内存管理

    C++__堆,栈与内存管理 1.什么是栈,什么是堆 具体可以看这篇:转载[C]堆区和栈区的区别 2.静态变量,全局变量,堆,栈生命周期 3.new与delete的动作 4.动态分配的内存计算 参考:& ...

  8. C++内存管理学习堆和栈

    来源:http://c.chinaitlab.com/basic/936306_2.html 一 C++内存管理 1.内存分配方式 在讲解内存分配之前,首先,要了解程序在内存中都有什么区域,然后再详细 ...

  9. 堆和栈的区别 (转贴)

    非本人作也!因非常经典,所以收归旗下,与众人阅之!原作者不祥! 堆和栈的区别 一.预备知识-程序的内存分配 一个由c/C++编译的程序占用的内存分为以下几个部分 1.栈区(stack)- 由编译器自动 ...

最新文章

  1. Swagger生成的接口需要权限验证的处理方法
  2. why the ebitda and ebit differ so much for netflix?
  3. windows server 2012 FTP 服务器 / 访问网络共享盘
  4. python下载器2
  5. 从excel表中生成批量SQL,将数据录入到数据库中
  6. 常见的攻击手段及其防御方式
  7. hadoop备战:hbase的分布式安装经验
  8. 即时通讯源码|IM源码PHP
  9. java 进销存系统_java进销存系统(含数据库)
  10. VHDL半整数与奇数分频器设计实验
  11. ddos源码 ntp_DDOS攻击之NTP放大攻击
  12. 谷歌搜索没有相机图标_关于Google图片网站不能以图搜图的解决方案
  13. [转载]使用 Apache Geronimo 和 JMS 构建事件驱动的框架
  14. 天龙八部怀旧服服务器维护,新天龙八部怀旧服游戏2月4日全服更新维护公告
  15. 免费壁纸背景高清图片素材网站
  16. linux音乐服务器mpd,OpenWRT中使用mpd作为音频Server及其使用
  17. C语言实现科学计算器
  18. oracle中涨工资,oracle涨工资问题
  19. java kinect 人体识别_基于kinect的人体动作识别系统
  20. Couchebase命令行工具cbq

热门文章

  1. c语言编程中如何对其,C语言内存对齐详解(3)
  2. 网络电视录制软件_有哪些比较好用的录音软件【精品合集】
  3. build tut framework on ubuntu
  4. 通过注册表修改打开方式
  5. matlab中箭头详细设置
  6. 各大厂商CTR广告预估模型的优缺点对比
  7. 结合上下文和篇章特征的多标签情绪分类
  8. c fun函数求n个整数的平均值_Python语法示例——函数
  9. 反射 java 例子 get_Java反射实例
  10. java日期加一天_Java 关于日期加一天(日期往后多一天)