博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

指针 new delete

Posted on 2009-06-15 15:39  浪端之渡鸟  阅读(487)  评论(0编辑  收藏  举报

++相比C#令人头痛也同样令痴迷于C++的程序员快慰的一点是对于指针的操作和对于内存的分配。不熟练的程序开发者往往会因指针和内存的不当操作而碰到许多问题。而娴熟的程序员确可以自如地定义指针的行为,自主分配/回收内存,使得程序代码的执行效率更高。
  内存的分配/回收有两种机制。其一是malloc()/free()函数,其二是new/delete运算符。malloc()/free()是C++/C语言的标准库函数,new/delete是C++的运算符。它们都可用于申请动态内存和释放内存。由于前者是标准库函数,后者是运算符,这决定了他们在实现内存管理时的差别所在。
  对于非内部数据类型的对象而言,光用malloc()/free()无法满足动态对象的要求。对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。由于malloc()/free()是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc()/free()。因此C++语言需要一个能完成动态内存分配和初始化工作的运算符new,以及一个能完成清理与释放内存工作的运算符delete。注意new/delete不是库函数,而是在C++中强化了的运算符。
  对于内部数据类型来说,由于内部数据类型的“对象”没有构造与析构的过程,对它们而言malloc()/free()和new/delete是等价的。既然new/delete的功能完全覆盖了malloc()/free(),为什么C++不把malloc/free淘汰出局呢?这是因为C++程序经常要调用C函数,而C程序只能用malloc/free管理动态内存。
  如果用free释放“new创建的动态对象”,那么该对象因无法执行析构函数而可能导致程序出错。如果用delete释放“malloc申请的动态内存”,理论上讲程序不会出错,但是该程序的可读性很差。所以new/delete必须配对使用,malloc/free也一样。
  简而言之:
  1.new/delete在实现上其实调用了malloc()/free()函数。
  2.new除了分配内存,还要调用构造函数;而malloc()只是分配内存。
  3.delete除了回收内存,还要调用析构函数;而free()只是回收内存。
  4.new/delete是C++中(而不是C中)的运算符,而malloc()/free()是C++/C中均有的函数。
  5.对于内部数据类型而言malloc()/free()和new/delete的作用等价;而对于需要构造和析构的非内部对象类型而言内存管理只能通过new/delete操作符。
  6.malloc()/free(),new/delete均应成对使用,防止内存泄漏。

  下面所讲的案例是作者在使用new/delete运算符时遇到的问题,发现网上有好多网友也遇到过这个问题,结合网路上的一些解答,现总结出来。

  下面是一段简短的程序代码:
  unsigned int ID;
  char * str=new char[3];
  for(ID=100;ID<110;++ID)
  {
     itoa(ID,str,10);
     printf("%s ",str);
  }  
  delete []str;
  该程序编译可以通过,但运行时会报错。原因在于我们申请的字符串str是三个字符长度,而三位数的整型数ID转换为字符串时为4个字符(3个数字,末尾一个空字符),所以实际上程序运行中写入str内存的内容容量大于str开辟的内存容量,这导致在用delete回收str内存时系统报错。这里如果把str字符串的长度开辟为4个字符长度,则程序是正确的。

    下面是一个网友所碰到的问题,因为具有代表性,所以也贴上来:
    char * p   =   new char;  
    int * i   =   new int;  
    p   =   "hi";  
    cout<< *p <<endl;  
    *i   = 123 ;  
    delete   i;   // 为何delete整型指针是正确的
    delete   p;   // 而delete字符串指针式错误的?
    关于这个问题,一个网友给出了合理的解释:出现这个错误的原因不是memory overflow而是memory lost。首先char* p = new char返回的是一个字节的指针;其次 p = "hi"把一个常量字符串的指针赋给了p,会导致p原来指向的内存被忽略,当程序最后用delete p去回收内存时实际上并不是回收由new所分配出来的那部分内存,结果造成内存泄漏。另外由于此时p指向了一个字符串常量"hi",语句delete p属于非法操作。

 

 

malloc()/free()C/C++中的函数

new/deleteC++中的操作符

它们都是用于内存的分配和回收,一般情况下在C++中提倡使用newdelete而不用mallocfree

mallocC的函数,在C++中可以继续使用,而new是在C++中引入的,可以说是malloc()的增强版,对于类来说,使用new还可以自动调用类的构造函数,这在某些场合非常重要。

new申请堆内存,malloc()也从申请内存(函数返回的指针是指向堆里面的一块内存。操作系统中有一个记录空闲内存地址的链表。当操作系统收到程序的申请时,就会遍历该链表,然后就寻找第一个空间大于所申请空间的堆结点,然后就将该结点从空闲结点链表中删除,并将该结点的空间分配给程序。)。

new分配内存给对象,可以引起的constructor构造函数的执行和而相应的析构函数destructor(用delete)会在类毁灭前的时候执行。  

malloc()不知道构造函数和析构函数,没有这些功能,最好不要用于对象的创建,在C中没有对象的概念,O(∩_∩)O

例子(C++)

string *string1 = <string*>(malloc(10 * sizeof(string))); //malloc 函数默认返回的是void * 类型,须强制转换

string *string2 = new string[10];

其结果是,string1确实指向的是可以容纳10string对象的足够空间,但内存里并没有创建这些对象(若有,其值将是随机的)。换句话说,string1其实一点用也没有。相反,string2指向的是一个包含10个完全构造好的string对象的数组,每个对象可以在任何读取string的操作里安全使用。

free(string1);

delete [ ] string2; //如果不加[ ]的话,只调用了一次析构函数,释放了一个string的内存空间!

调用free()将会释放string1指向的内存,但内存里的string对象不会调用析构函数,指针string1的值并没有改变,还是指向原来的地方,free()只是系统将内存标识为可用,但并不自动将指针赋NULL,也不清空内存。如果string对象象一般情况那样,自己已经分配了内存,那这些内存将会全部丢失(内存中的对象还是存在的,只是你无法得到一个指向它们的指针了,无法使用它们)。相反,当对string2调用delete时,数组里的每个对象都会在内存释放前调用析构函数,将指针设NULL,并把内存清空。

new ==分配内存(malloc)+初始化(调用构造函数)

delete ==调用析构函数+释放内存(free)

free掉的内存是该指针指向的一段内存空间,里面应该是空的。而delete掉的内存是里面确实存有数据或者对象。

如果用free释放“new创建的动态对象”,那么该对象因无法执行析构函数而可能导致程序出错。如果用delete释放“malloc 申请的动态内存”,理论上讲程序不会出错,但是该程序的可读性很差。养成一个良好的习惯就是严格的配对使用:new/deletemalloc/free  

比如你的对象较小而且应用程序经常动态地newdelete对象。程序运行一段时间后会产生许多内存碎片。所以你要写自己的内存管理来防止内存碎片。你可能以用malloc要一大块内存,然后根据需要再在上面建许多的对象。当整个内存块上没有对象时,你可以把那一大块内存free掉。总的来说,当你不需要调用构造函数时用malloc,反之则用new