20170816_new和delete的使用

20170816_new和delete的使用

转自:http://blog.csdn.net/zmyoukonw/article/details/43164303##1


  关于动态内存分配,我们在c中经常会使用到malloc/calloc/realloc/free这四个函数,并且这四个函数很有用。但是c++之父推荐c++程序猿(不是c程序猿)使用更加高明的new与delete,因为他们比malloc与free有很多好处(我们在本文最后部分将会提到)。

———————————————————华丽的分割线————————————————————————

一、先了解内存

       为什么我要先讲内存的概念呢?因为程序离不开内存。new与delete的操作只是在内存的堆区中操作。不了解内存,就永远掌握不了程序的精髓,更谈不上真正的理解程序。要说及内存的问题,我觉得可以出一本书,但是本文只了解与程序猿密切相关的部分。

       我们这里说的内存,不是真正意义上的物理内存(RAM或者你理解的主板上的内存条),而是虚拟内存。学过计算机原理或者操作系统的同学,对于虚拟存储技术肯定不陌生。因为物理内存的空间有限(现在普通电脑标配4G已经不错了),所以引入了虚拟内存机制

       一般地,每个进程(程序运行起来了才算进程)先天拥有0 -- 4G(32位系统)的虚拟内存地址。虚拟内存地址本质上是一个数字,并不是真正的内存,只有通过映射到物理内存之后才能存放数据。程序猿接触到的任何内存地址,其实都知识虚拟内存地址。

       这0 -- 4G的虚拟内存,分为 用户空间(0 -- 3G) 和 内核空间(3G -- 4G,大小可变)。我们一般只能操作用户空间,但是也可以通过调用一些系统函数来访问内核空间。

       这0--3G的用户空间被分为六个部分,我们按内存地址的升序来依次介绍:

           1.代码区:用于存储代码。函数指针就是指向代码区。是只读区

           2.只读常量区:字符串的 字面值(“”括起来的)和 const修饰的全局变量都在只读常量区。该区域 只读,修改只读敞亮区会引发“段错误”。

           3.全局区:用于存储全局变量static修饰的局部变量也在全局区,全局区在main函数运行之前被创建,程序结束前才被销毁。

           4.BBS段:用于存储 未初始化的全局变量,BBS段和全局区的唯一区别在于:在main函数运行之前,BBS段会自动清0.

           5.堆区(heap):也叫自由区,是程序猿唯一能自由管理的区域,系统是不会自动管理堆区的。堆区的内存分配和回收都是由程序猿们完成的,堆区如果只分配不回收会造成内存泄露。

           6.栈区(stack):存储局部变量(非static类型),包括:函数的形参,局部变量和块变量。栈区系统会自动管理。(该区接近3G部分的内存地址)


       那么我们今天要讲到的动态内存分配的new 和delete 操作的内存部分就是:堆区

二、new与delete的使用方法

1、使用new来分配堆内存,用delete来释放new出来的堆内存。

  格式:   类型 *指针名 = new 类型;
                     类型 *指针名 = new 类型(初值);
                     delete 指针名;
  一定要注意,new 和delete 的都是在操作指针!

我们来看程序:

#include <iostream>  
using namespace std;  
int main()
{  
  int* a = new int;  
    cout<<"a的地址:"<<a<<"a的值:"<<*a<<endl;  
  int* b = new int(2);  
  cout<<"b的地址:"<<b<<"b的值:"<<*b<<endl;  
  delete a;  
  delete b;  
    return 0;  
}


我们动态的在堆区申请了两块内存,并用指针a和b指向这块新分配的内存。

我们对b进行了初始化但是没有对a进行初始化,注意这并不代表对没有初始化的a指向地址所存放的值一定是0。  

2、使用new和delete申请连续的堆内存(一维):

  格式:  类型 *指针名 = new 类型[n];
               delete [ ] 指针名;
  注意不要忘记了delete后面的“[ ]”符号。无论你创建了几维的空间,都只要在delete后面加一个“[ ]”就行。

  我们来看代码:

#include <iostream>  
using namespace std;  
int main()
{  
   int* arr = new int[5];  
   for(int i = 0;i<5;++i)  
       arr[i] = i+1;  
   for(int i = 0;i<5;++i)  
       cout<<arr[i]<<" ";  
   cout<<endl;  
   delete[] arr;  
   return 0;  
}



3、通过new来分配N 维数组

  通过new操作符分配的N维数组,返回N - 1维数组指针。
       例如:
  Int (*arr)[4] = new int[3][4];
  Int (*arr)[4][5] = new int[3][4][5];

4、定位分配内存

  什么是定位分配?就是在一个已分配的内存空间中创建对象。可以这样理解:你爸壕的买了一大块地皮,说允许你随便在上面建房子。那你修房子的地址,只能是在这块已经买下了空间中。
  格式: new (指针)类型(初值);
  我们来看一段有意思的代码:

#include <iostream>  
#include <cstdio>  
using namespace std;  
int main()
{  
   char str[4];  
   int* arr = new (str) int(0x12345678);  
   printf("%#x, %#x, %#x, %#x\n",str[0],str[1],str[2],str[3]);  
   return 0;  
} 
  

你们猜猜输出的结果会是什么样呢?
  我们一起来分析一下这段代码再看输出结果吧!
  首先,我们建立了一个char型的数组str,有四个内存空间也就是4个字节(注意int就是4个字节哦)。然后我们通过定位内存分配的方法,先将要分配的内存的首地址定位到了str数组的首地址处。说明我们接下来分配的内存都是在str的内存空间中操作的。我们新建的int型数字的初始值为0x12345678,将四个字节分开后,每个字节上的值为0x78,0x56,0x34,0x12,我们再查看str数组的每个元素(一个字节),我想答案应该就是那几个数字了吧?(十六进制)。我们一起来看看我们的猜测对不对吧:


三、new 与delete 的应用

  我们通过new和delete来动态分配内存,最主要的用途的莫过于创建动态数组吧?
  我们都知道定义数组时,数组的大小必须为常量,不能通过一个变量来动态决定数组的大小。

那么,我们可以使用new和delete克服这个困难。
  
  我们先来看看怎样动态创建一个一维数组:

#include <iostream>  
using namespace std;  
int main()
{  
  int n;  
  cout<<"请输入你想要的数组大小:";  
  cin>>n;  
  int* arr = new int[n];  
  cout<<"请输入数组的内容:";  
  for(int i=0; i<n; ++i)  
       cin>>arr[i];  
  cout<<"数组为:"<<endl;  
  for(int i=0; i<n; ++i)  
       cout<<arr[i]<<" ";  
  cout<<endl;  
  delete[] arr;  
  return 0;  
} 



  
  我们动态的输入数组的大小n,并使用new 来动态的在堆区中分配出一个连续的n个内存空间。

就可以像数组一样的使用这块连续的内存空间。不过,用完了记得释放内存空间哦,不然一直占用着进程的内存,会造成内存泄露。
  

那我们怎样创建一个二维数组呢?

你说,简单,这样写:int** arr = new int[m][n];看看编译器怎么说?



  心里那个郁闷啊!那应该怎么写呢?
  写之前,我觉得我们应该先想清楚二维数组的逻辑关系。你想啊,二维数组是不是一个一级指针的数组。这个数组中的每个元素是一个指针,这个指针指向一个数组的首地址,这样不就是一个二维数组了吗?
  例如:我们需要建立一个mxn的二维数组。那我们就先动态创建一个含有m个元素的一维的指针数组,再分别对数组中每个指针动态分配一个n个空间大小的数组。这样就可以组成了一个二维数组。我们一起来动手写写吧。

#include <iostream>  
using namespace std;  
int main()
{  
   int m=2,n=3;  
   int **arr = new int *[m];  

   for(int i =0; i<m; ++i)  
       arr[i] = new int[n];  
   for(int i =0; i<m; ++i)  
       for(int j=0; j<n; ++j)  
           arr[i][j] = i * m + j; 
 
   for(int i =0; i<m; ++i)
    {  
       for(int j=0;j<n;++j)  
           cout<<arr[i][j]<<" ";  
       cout<<endl;  
   }  
  delete[] arr;  
  return 0;  
}


通过上面程序我们发现,

arr是一个二级指针。int** arr = new int*[m],先是为*arr分配一个m大小的空间。

再通过循环,为每个arr再创建一个n个大小的空间。


  如果要创建N维的数组,方法同二维,就是多几个for循环而已。
  当然如果你有更好的动态创建N维数组的方法,请赐教!

四、使用new和delete应注意什么?

1、不能通过delete 操作符释放已经释放过的内存。

  明知山有虎,偏向虎山行!生活中不泛有这样的勇士。你说不能,我偏要试试。来,我陪你一起来试试这样的后果是什么?

#include <iostream>  
using namespace std;  
int main()
{  
  int* a = new int;  
  cout<<”a的地址:”<<a<<endl;  
   delete a;  
   delete a;  
  return 0;  
}

   编译器会放过你,不给你报错,可是运行起来,程序就挂了。提示说你double free(两次释放)了同一个地址。

不想你的程序挂彩的话,还是注意这一点哦。

  

2、delete 野指针的后果未知,delete 空指针安全。

  为什么delete 野指针的后果未知呢? 因为你也不知道野指针指向哪块内存地址,可能这个地址存在可能不存在,存在的话delete掉它是可以的,如果不存在,就会像上面那样程序挂掉。
  所以我们养成一个好习惯,delete 掉一个指针后,这个指针变成了野指针,为了避免它干坏事,我们把这个指针指向NULL。delete一个NULL空指针是绝对安全的!


#include <iostream>  
using namespace std;  
int main()
{  
  int* a;  
  delete a;        //野指针,一般会出错
  a = new int(2);  
  delete a;        //new 出来的地址,肯定没问题
    a = NULL;  
    delete a;        //空指针,肯定没问题
} 

这段代码运行后没有出现错误:

第6行的delete 的是a这个野指针,还好没有出错。

第8行delete是一个new出来的地址,当然没有问题啦。

第10行我们delete了一个空指针,当然也是木有问题的啦!

3、内存分配失败后 new 操作符抛出 bad_alloc 异常。

  我们都知道进程的内存一般只有4G,而堆区的内存可没有那么大,不可能你想要多大的内存就能分配出多大的内存。所以如果分配的内存过大,系统提供不了的话是会抛出异常提示你分配失败的。
  
  在c语言中我们使用malloc 来分配内存,如果失败的话malloc 会返回一个NULL 指针。而在c++中,如果new 分配内存失败,会抛出 bad_alloc 异常。
  在c++中有一套异常处理机制(try 和catch),如果你不懂的话,我在以后在为你讲解,你先看我怎么用的吧。

#include <iostream>  
#include <cstdlib>  
using namespace std;  
int main()
{  
    int* a = (int*)malloc(sizeof(int) * 0xFFFFFFFF);  
    if(!a)  
        cout<<"a malloc分配内存失败!"<<endl;  
    try
     {  
        int* b = new int[sizeof(int) * 0xFFFFFFFF];  
        cout<<"b new分配内存成功!"<<endl;  
        delete[] b;  
        cout<<"b delete释放内存成功!"<<endl;  
     }  
     catch(exception& ex)
     {  
        cout<<"b new分配内存失败!"<<endl;  
        cout<<ex.what()<<endl;  
     }  
     return 0;  
}



  我给a分配了4*4G(2的32次方) = 16G 的内存,够大吧!

(一开始我分配的4G内存,居然分配成功了!我后来想到我用的64位系统。。。。。)

malloc失败后把a变为NULL指针。我给b同样分配了16G的内存,肯定分配失败啊,所以系统在new结束之后抛出了bab_alloc异常,catch部分马上捕获到了该异常,并显示对该异常进行了处理(自定义的处理)。

——————————————————————————————结束语——————————————————————————————
  其实new和delete没有多少难的知识点。

        但是大家一定要重新认识内存和程序的关系!!!

        得内存者得天下!!!


  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值