对于计算机程序设计而言,变量和对象在内存中的分配都是编译器在编译程序时安排好的,这带来了极大的不便,如数组必须大开小用,指针必须指向一个已经存在的变量或对象。对于不能确定需要占用多少内存的情况,动态内存分配解决了这个问题。
一、new/delete 简介
new 和 delete 是 C++ 用于管理 堆内存 的两个运算符,对应于 C 语言中的 malloc 和 free,但是 malloc 和 free 是函数,new 和 delete 是运算符。除此之外,
new 在申请内存的同时,还会调用对象的构造函数,而 malloc 只会申请内存;同样,delete 在释放内存之前,会调用对象的析构函数,而 free 只会释放内存。
new 运算符的内部实现分为两步:
-
内存分配(先)
调用相应的
operator new(size_t)
函数,动态分配内存。如果operator new(size_t)
不能成功获得内存,则调用new_handler()
函数用于处理new失败问题。如果没有设置new_handler()
函数或者new_handler()
未能分配足够内存,则抛出std::bad_alloc
异常。“new运算符”所调用的operator new(size_t)
函数,按照C++的名字查找规则,首先做依赖于实参的名字查找(即ADL规则),在要申请内存的数据类型T的 内部(成员函数)、数据类型T定义处的命名空间查找;如果没有查找到,则直接调用全局的::operator new(size_t)
函数。 -
构造函数(后)
-
在分配到的动态内存块上 初始化 相应类型的对象(构造函数)并返回其首地址。如果调用构造函数初始化对象时抛出异常,则自动调用
operator delete(void*, void*)
函数释放已经分配到的内存。
这是string *s = new string("a value"); 这句表达式内部的实现:
我们可以看出new内部的调用顺序:(初始化一个对象时)
new内部的调用顺序:(初始化若干个对象时)
delete 运算符的内部实现分为两步:
-
析构函数(先)
调用相应类型的析构函数,处理类内部可能涉及的资源释放。
-
内存释放(后)
调用相应的
operator delete(void *)
函数。调用顺序参考上述operator new(size_t)
函数(ADL规则)。
同样地,delete对象时,调用顺序为:(delete单个对象时)
delete对象时,调用顺序为:(delete多个对象时)
二、new/delete 表达式语法
2.1 new 表达式语法
一、new用法
1.开辟单变量地址空间
使用new运算符时必须已知数据类型,new运算符会向系统堆区申请足够的存储空间,如果申请成功,就返回该内存块的首地址,如果申请不成功,则返回零值。
new运算符返回的是一个指向所分配类型变量(对象)的指针。对所创建的变量或对象,都是通过该指针来间接操作的,而动态创建的对象本身没有标识符名。
一般使用格式:
格式1:指针变量名=new 类型标识符;
格式2:指针变量名=new 类型标识符(初始值);
格式3:指针变量名=new 类型标识符 [内存单元个数];
说明:格式1和格式2都是申请分配某一数据类型所占字节数的内存空间;
格式2在内存分配成功后,同时将一初值存放到该内存单元中;
格式3可同时分配若干个内存单元,相当于形成一个动态数组。
1)new int; //开辟一个存放整数的存储空间,返回一个指向该存储空间的地址。int *a = new int 即为将一个int类型的地址赋值给整型指针a
2)int *a = new int(5) 作用同上,但是同时将整数空间赋值为5
比较经典的几个例子(结合指针来理解一下):
int *p=new int; (*p)才是指针
int **p =new int *;
int **p=new int *[3];//指针数组
int (*p)[3]=new int[2][3];//int类型的二维数组(),我们分析正常的二维数组可以知道,对于int a[2][3],可以看作int [3] a[2],即有一个一维数组,里面有两个元素,每个元素的类型都是int [3]
int * (*p)[3]=new int *[2][3];//int指针类型的二维数组
node *p=new node[2];//结构体数组
2.开辟数组空间
对于数组进行动态分配的格式为: `
delete a; //释放单个int的空间
2. 删除数组空间
int *a = new int[5];
delete []a; //释放int数组空间
记住:new和delete就像malloc和free一样,都要成对使用哦。
三、new/delete 和malloc/free的区别和联系
2.1 new 表达式语法
一、new用法
new和delete与malloc和free一样,都是存在堆上的。那么,二者有什么异同呢?
总结new/delete和malloc/free的区别和联系:
1. 它们都是动态管理内存的入口。
2. malloc/free是C/C++标准库的函数,new/delete是C++操作符。
3. malloc/free只是对内存进行分配和释放;new/delete还负责完成了创建和销毁对象的任务。
4.new的安全性要高一些,因为他返回的就是一个所创建的对象的指针,对于malloc来说返回的则是void*,需要手动计算类型大小,进行强制类型转换,显然这是一个危险的漏洞。
5.new/delete的底层调用了malloc/free。
6.malloc/free申请空间后得判空,new/delete则不需要。
7.new直接跟类型,malloc跟字节数个数。
8.我们可以对new/delete重载,使内存分配按照我们的意愿进行,这样更具有灵活性,malloc则不行。
不过,new/delete也并不是十分完美,大概最大的缺点就是效率低(针对的是缺省的分配器),原因不只是因为在自由存储区上分配(和栈上对比),而且new只是对于堆分配器(malloc/realloc/free)的一个浅层包装,没有针对小型的内存分配做优化。另外缺省分配器具有通用性,它管理的是一块内存池,这样的管理往往需要消耗一些额外空间。我们可以针对new/delete进行重写以追求更高的效率,对于这方面更深入的探讨可以参考《Effective C++》第八章。
四.使用时的注意事项
1. new 和delete都是内建的操作符,语言本身所固定了,无法重新定制,想要定制new和delete的行为,徒劳无功的行为。
2. 动态分配失败,则返回一个空指针(NULL),表示发生了异常,堆资源不足,分配失败。
3. 指针删除与堆空间释放。删除一个指针p(delete p;)实际意思是删除了p所指的目标(变量或对象等),释放了它所占的堆空间,而不是删除p本身(指针p本身并没有撤销,它自己仍然存在,该指针所占内存空间并未释放),释放堆空间后,p成了空指针。
4. 内存泄漏(memory leak)和重复释放。new与delete 是配对使用的, delete只能释放堆空间。如果new返回的指针值丢失,则所分配的堆空间无法回收,称内存泄漏,同一空间重复释放也是危险的,因为该空间可能已另分配,所以必须妥善保存new返回的指针,以保证不发生内存泄漏,也必须保证不会重复释放堆内存空间。
5. 动态分配的变量或对象的生命期。我们也称堆空间为自由空间(free store),但必须记住释放该对象所占堆空间,并只能释放一次,在函数内建立,而在函数外释放,往往会出错。
6. 要访问new所开辟的结构体空间,无法直接通过变量名进行,只能通过赋值的指针进行访问。
用new和delete可以动态开辟和撤销地址空间。在编程序时,若用完一个变量(一般是暂时存储的数据),下次需要再用,但却又想省去重新初始化的功夫,可以在每次开始使用时开辟一个空间,在用完后撤销它。
参考资料:
http://blog.csdn.net/hihozoo/article/details/51441521(placement new 运算符,在栈上new对象可参考)
http://blog.csdn.net/xxpresent/article/details/53024555