C++ New与Delete

来源:互联网 发布:弯曲法测杨氏模量数据 编辑:程序博客网 时间:2024/06/05 07:03

new与delete

【1】malloc与free  和 new与delete

(1)malloc与free是C++/C语言的标准库函数。new/delete是C++的运算符。它们都可以申请动态内存和释放内存。

(2)对于非内部数据类型的对象而言,用malloc/free无法满足动态对象的要求(对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数)。

(3)由于malloc/free是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/free。因此,C++语言需要可以完成动态内存分配和初始化工作的运算符new,以及一个能完成清理与释放内存工作的运算符delete。

(4)都是在堆(heap)上进行动态的内存操作。用malloc函数需要指定内存分配的字节数并且不能初始化对象。new会自动调用对象的构造函数。

delete 会调用对象的destructor,而free 不会调用对象的destructor。

【2】描述内存分配方式以及它们的区别?

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

(2)在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集。

(3)从堆上分配(动态内存分配)。程序在运行的时候用malloc 或 new 申请任意多少的内存,程序员自己负责在何时用free 或delete 释放内存。

动态内存的生存期由程序员决定,使用非常灵活,但问题也最多。

【3】malloc 与 free

(1)malloc函数分配的空间一定要用free函数释放掉。

(2)free(p) 仅仅指释放了malloc分配的空间,但是p指针仍然不为空,所以,在free函数释放后一般要置空!防止野指针!

(3)非空指针只可以释放一次。

(4)一般两者搭配使用。

(5)malloc 与 free示例代码如下:

复制代码
 1 #include<iostream> 2 #include<malloc.h> 3 using namespace std; 4  5 void main() 6 { 7     int *p = NULL ;  //指针定义最好初始化为空(程序员基本素养,哈哈) 8  9     //空指针释放多次没有任何意义10     free(p);   //编译通过!运行通过!一次11     free(p);   //编译通过!运行通过!二次12 13     p = (int *)malloc(sizeof(int)*5);14     if(NULL == p)15     {16         cout<<"malloc failed!"<<endl;17         exit(1);18     }19     else20     {21         p[0] = 0;       //注意赋值形式22         p[4] = 4;23 //        p[5]=100;       //编译可以通过,但是运行错误error!!!因为p[5]越界24 25         cout<<"p[4]: "<<p[4]<<endl;   //426           27         cout<<"p[1]: "<<p[1]<<endl;   //随机数!!28      }29 30     free(p);  //malloc申请空间使用free释放(固定搭配)31 32     if(NULL == p)33     {34         cout<<"free(p) p == NULL"<<endl;35     }36     else37     {38         cout<<"free(p) p != NULL"<<endl;   //野指针!!!39 //        p[2] = 100;  //编译可以通过,运行时崩溃!!因为野指针所致。40     }41 42 //    free(p);    //编译可以通过,运行时崩溃!! 因为已经释放了一次,再次释放导致错误。43 44     p = NULL;  //彻底预防了它的破坏性45 46     //........
复制代码

【4】new 与 delete

(1)new的三种形态

到目前为止,C++相关资料书籍谈及的new至少代表以下三种含义:

<1>new operator : new 运算符 (当然,书面称法。个人觉得还是按照习惯称作关键字new,以下此种形态均称关键字 new

<2>operator new : 操作符  new(当然,书面称法。个人觉得称为new函数,以下此种形态均称new函数

<3>placement new: 定位 new(C++primer上的称法)

(2)关键字new

平常我们使用最多的就是关键字new。它由语言内建 ,不能重载 ,不能改变其行为 。

关键字new在堆上动态创建一个对象时,它实际上做了三件事:

1:获得一块内存空间、

2:调用构造函数、

3:返回正确的指针。

当然,如果我们创建的是内置类型的变量,那么第二步会被省略。

示例代码如下:

复制代码
 1 #include<iostream> 2 #include<assert.h> 3 using namespace std; 4  5 class A  6 {  7     int i;  8 public:  9     A(int _i = 2) :i(_i*_i) 10     {11         cout<<"constructor   "<<this<<endl;12     } 13     void Print() 14     { 15         cout<<i<<endl; 16     } 17     ~A()18     {19         cout<<"destructor  "<<this<<endl;20     }21 22 }; 23 24 void main()25 {26     /*27      *内置类型示例代码28      */29     int *p1 = NULL;30     p1 = new  int(10);31     assert(p1 != NULL);32     cout<<*p1<<endl;     //1033     delete  p1;34     p1 = NULL;35 36 37     int *p2 = NULL;38     p2 = new  int[5];   //申请5份int类型大小的空间39     assert(p2 != NULL);40     delete []p2;       //释放数组变量41     p2 = NULL;42 43     /*44     *自定义类型示代码45     */46     A  *p3 = NULL;47     p3 = new A;       //调用默认复合构造函数48     assert(p3 != NULL);49     p3->Print();      //450     cout<<"delete obj"<<endl;51     delete p3;52     p3 = NULL;53 54     A  *p4 = NULL;55     p4 = new A[5];       //调用默认复合构造函数   注意数组56     assert(p4 != NULL);57     p4[0].Print();    //458     cout<<"delete obj"<<endl;59     delete []p4;60     p4 = NULL;61 62     A  *p5 = NULL;63     p5 = new A(10);       //调用默认复合构造函数   注意区别64     assert(p5 != NULL);65     p5[0].Print();    //10066     cout<<"delete obj"<<endl;67     delete p5;68     p5 = NULL;69 70 }71 72 /*73 1074 constructor   00644B4875 476 delete obj77 destructor  00644B4878 constructor   00644BDC79 constructor   00644BE080 constructor   00644BE481 constructor   00644BE882 constructor   00644BEC83 484 delete obj85 destructor  00644BEC86 destructor  00644BE887 destructor  00644BE488 destructor  00644BE089 destructor  00644BDC90 constructor   00644B4891 10092 delete obj93 destructor  00644B4894  */
复制代码

(3)函数new

关键字new第一步分配内存实际上是通过调用new函数来完成的,而这里的new就是像加减乘除一样的操作符,因此是可以重载的。

new函数默认情况下首先调用分配内存的代码,尝试得到一段堆上的空间,如果成功就返回;如果失败,则转而去调用一个new_hander,然后继续重复前面过程。

如果我们对这个过程不满意,就可以重载operator new,来设置我们希望的行为。

示例代码如下:

复制代码
  1 #include<iostream>  2 #include<assert.h>  3 #include<new>  4 using namespace std;  5   6 class A   7 {   8     int i;   9 public:  10     A(int _i = 2) :i(_i*_i)  11     { 12         cout<<"constructor   "<<this<<endl; 13     }  14     void Print()  15     {  16         cout<<i<<endl;  17     }  18     ~A() 19     { 20         cout<<"destructor  "<<this<<endl; 21     } 22  23 };  24 /* 25  *重载全局new/delete函数 26  */ 27 void * operator new(size_t size) 28 { 29     cout<<" overload operator new "<<endl; 30     void *p = malloc(size); 31     return (p); 32 } 33 void operator delete(void *p) 34 { 35     cout<<" overload operator delete "<<endl; 36     free(p); 37 } 38  39 void main() 40 { 41     /* 42      *内置类型示例代码 43      */ 44     int *p1 = NULL; 45     p1 = (int *)::operator new(sizeof(int)); 46     new(p1) int(10);   //第一种赋值方式   47     assert(p1 != NULL); 48     cout<<*p1<<endl;       //10 49     ::operator delete(p1); 50     p1 = NULL; 51  52  53     int *ptr = NULL; 54     ptr = (int *)::operator new(sizeof(int)); 55     *ptr = 100;    //第二种赋值方式   56     assert(ptr != NULL); 57     cout<<*ptr<<endl;      //100 58     delete  ptr; 59     ptr = NULL; 60  61     int *p2 = NULL; 62     p2 = (int *)::operator new(sizeof(int) * 5);   //申请5份int类型大小的空间 63     assert(p2 != NULL); 64     for(int i = 0; i < 5; ++i) 65     { 66         p2[i] =  i + 10;   //数组变量的赋值 67     } 68     for(int i = 0; i < 5; ++i) 69     { 70         cout<<p2[i]<<endl;   //10 11 12 13 14  71     } 72     ::operator delete[] (p2);       //释放数组变量 73     p2 = NULL; 74  75     /* 76     *自定义类型示代码 77     */ 78     A  *p3 = NULL; 79     p3 = (A *)::operator new(sizeof(A));      80     assert(p3 != NULL); 81     new(p3) A(10); 82     p3->Print();       //100 83     cout<<"delete obj"<<endl; 84     p3->~A();    //先调用对象析构函数 85     ::operator delete(p3);  //再释放申请内存 86     p3 = NULL; 87  88     //注意差别 89     A  *p4 = NULL; 90     p4 = (A *)::operator new(sizeof(A));      91     assert(p4 != NULL); 92     new(p4) A(10); 93     p4->Print();       //100 94     cout<<"delete obj"<<endl; 95     ::operator delete(p4);  //直接释放申请内存 96     p4 = NULL; 97 } 98  99 /*100 overload operator new101 10102 overload operator delete103 overload operator new104 100105 overload operator delete106 overload operator new107 10108 11109 12110 13111 14112 overload operator new113 constructor   00194B48114 100115 delete obj116 destructor  00194B48117 overload operator delete118 overload operator new119 constructor   00194B48120 100121 delete obj122 overload operator delete123 124  */
复制代码

下面比较new关键字与new函数的区别:

<1>new关键字

int *ptr = new  int(100);

1:分配内存; 2:赋初始值; 3:类型自动匹配; 4:大小自动。

<2>new函数

int *ptr = (int *)::operator new(sizeof(int) * 5);

1:分配内存; 2:无初始化; 3:类型转换; 4:大小手动。

(4)定位new

定位new是用来实现定位构造的,因此可以实现关键字new三步操作中的第二步,也就是在取得了一块可以容纳指定类型对象(变量)的内存后,在这块内存上构造一个对象(变量)。

示例代码如下:

关于对象的构建,上面new函数的示例代码中已经很具体了。在此,特别示例定位new也可以构造栈上的内存。

复制代码
 1 #include<iostream> 2 #include<assert.h> 3 //#include<new.h>   //有些资料书提醒必须加这个头文件,VS2010下可以省略。 4 using namespace std; 5  6 class A  7 {  8     int i;  9 public: 10     A(int _i = 2) :i(_i*_i) 11     {12         cout<<"constructor   "<<this<<endl;13     } 14     void Print() 15     { 16         cout<<i<<endl; 17     } 18     ~A()19     {20         cout<<"destructor  "<<this<<endl;21     }22 23 }; 24 25 26 void main()27 {28     char s[sizeof(A)]; 29     A* p = (A*)s; 30     new(p) A(3);   //定位new的用法31     p->Print(); 32     p->~A();  //不过必须要显式调用析构函数33 }34 35 /*36 constructor   0038F8EC37 938 destructor  0038F8EC39  */
复制代码

这里“new(p)   A(3)”这种奇怪的写法即是定位new的用法,它实现了在指定内存空间用指定类型的构造函数来构造一个对象的功能,后面A(3)就是对构造函数的显式调用。

通过上面的例子以及这个例子,我们可以看到这块指定的地址既可以是栈,又可以是堆,定位new对此不加区分。

但是,除非特别必要,不要直接使用定位new ,这毕竟不是用来构造对象的正式写法,只不过是new函数的一个步骤而已。

使用关键字new地编译器会自动生成对定位new的调用的代码,因此也会相应的生成使用delete时调用析构函数的代码。

如果是像上面那样在栈上使用了定位new,则必须手工调用析构函数,这也是显式调用析构函数的唯一情况: p->~A();

当我们觉得默认的关键字new对内存的管理不能满足我们的需要,而希望自己手工的管理内存时,定位new就有用了。

STL中的allocator就使用了这种方式,借助定位new来实现更灵活有效的内存管理。 

【5】new的基本使用指南

(1)如果想在堆上建立一个对象,应该用关键字new 。它既分配内存又为对象调用构造函数。

(2)如果仅仅想分配内存,就应该调用 new 函数;它不会调用构造函数。

如果想定制在堆对象被建立时的内存分配过程,你应该写自己的new 函数,然后使用new关键字, new 关键字会调用定制的 operator new .

(3)如果想在一块已经获得指针的内存里建立一个对象,应该用 定位new 。定位new 主要适用于:

<1>在对时间要求非常高的应用程序中,因为这些程序分配的时间是确定的;

<2>长时间运行而不被打断的程序;

<3>以及执行一个垃圾收集器(garbage collector)。

http://www.cnblogs.com/Braveliu/archive/2013/01/10/2854211.html

原创粉丝点击