前文已更新const与static关键字,今天来总结一下C++语言中用于动态开辟与释放内存的new与delete关键字!
const关键字C++学习——const关键字-CSDN博客
static关键字C++学习——static关键字-CSDN博客
其它关键字C++学习——sizeof、ertern、volatile等其它关键字-CSDN博客
一、new delete实现原理
1.1 new内存分配
对于简单类型,new[]计算好大小直接调用operator new分配内存;
对于复杂结构,new[]先调用operator new[]分配内存,然后在p的前四个字节额外存储数组大小n,然后调用n次构造函数,在delete[]时就可以取出这个保存的数,就知道了需要调用析构函数多少次了。
流程如下:
①new表达式调用一个名为operator new(operator new[])函数,分配一块足够大的、原始的、未命名的内存空间;
②编译器运行相应的构造函数以构造这些对象,并为其传入初始值;
③对象被分配了空间并构造完成,返回一个指向该对象的指针。
1.2 delete释放内存
delete简单数据类型默认调用free函数,delete和delete[]等同;
复杂数据类型先调用析构函数再调用operator delete[],假设指针p指向new[]分配的内存。因为要4字节存储数组大小,实际分配的内存地址为[p-4],系统记录的也是这个地址。delete[]实际释放的就是p-4指向的内存,而delete会直接释放p指向的内存,这个内存根本没有被系统记录,所以会崩溃。
函数:
(1)new operator:用于申请内存和初始化对象。
delete operator:完成析构对象和释放内存的操作。
(2)operator new:类似于C语言中的malloc,只是负责申请内存
placement new:用于在给定的内存中初始化对象。
operator delete 用于内存的释放,与C语言中的free相似。
二、malloc和new、free和delete的相同点与不同点
相同:
new/delete,malloc/free都是动态分配内存,对内存进行管理的方式
区别:
(1)new/delete 是C++关键字、操作符,需要编译器支持,malloc/free是库函数,需要头文件支持,C、C++中都可以使用。
(2)使用new操作符申请内存分配时无须指定内存块的大小,编译器会根据类型信息自行计算。而malloc则需要显式地指出所需内存的尺寸。
(3) new、delete 返回的是对象类型的指针,类型严格与对象匹配,无须进行类型转换,故new是符合类型安全性的操作符,malloc、free 返回的是 void 指针,需要通过强制类型转换将void*指针转换成所需类型。
(4)new内存分配失败时,会抛出bac_alloc异常。malloc分配内存失败时返回NULL。
(5)new可以调用对象的构造函数,对应的 delete 调用相应的析构函数。 malloc 仅仅分配内存,free 仅仅回收内存,并不执行构造和析构函数
注意:malloc 申请的内存空间要用free 释放,而new申请的内存空间要用 delete释放,不要混用。delete和free被调用后,内存不会立即回收,指针也不会指向空,由于没有重新对这块内存进行写操作,所以内存中的变量数值并没有发生变化,出现野指针的情况,因此,释放完内存后,应该将该指针指向NULL。
问题:既然new/delete的功能完全覆盖了malloc/free,为什么C++还保留malloc/free呢?
答:因为C++程序经常要调用C函数,而C程序只能用malloc/free管理动态内存。
三、malloc申请的存储空间能用delete释放吗?
不能用在动态类上,malloc /free主要为了兼容C。
malloc /free的操作对象都是必须明确大小的,而且不能用在动态类上。new 和delete会自动进行类型检查和大小,malloc/free不能执行构造函数与析构函数,在动态类是不行的。
四、new可以搭配free吗?
可以,但不安全,通过 free调用释放new申请的内存并不总是能正确的释放所有申请的内存。因为使用free方法释放内存时并不会调用实例的析构函数,此时如果实例中有动态申请的内存将因为析构函数没有被调用而没有得到释放,从而导致内存泄漏。
五、delete与delete[]
1.delete只会调用一次析构函数,而delete[]会调用每一个成员的析构函数,然后调用operator delete来释放内存。delete与new配套,delete []与new []配套
2.对于内建简单数据类型,没有析构函数,delete和delete[]功能是相同的;自定义的复杂数据类型delete和delete[]不能互用,delete[]删除一个指针数组,delete删除一个指针,如果用delete时没用括号,会认为删除的是单个对象。
3、动态数组管理new一个数组时,[]中必须是一个整数,但是不一定是常量整数,普通数组必须是一个常量整数;
4、new动态数组返回的并不是数组类型,而是一个元素类型的指针;
5、delete[]时,数组中的元素按逆序的顺序进行销毁;
6、new的机制是将内存分配和对象构造组合在一起,delete将对象析构和内存释放组合在一起的。
六、在成员函数中调用delete this会出现什么问题?
1.在类对象的内存空间只有数据成员和虚函数表指针,类的成员函数单独放在代码段中。
在调用成员函数时,隐含传递一个this指针,让成员函数知道是哪个对象在调用它,调用delete this时类对象的内存空间被释放,在delete this之后进行的其他任何函数调用,只要不涉及到this指针的内容,都能够正常运行。一旦涉及到this指针,如操作数据成员,调用虚函数等,就会出现不可预期的问题。
2.为什么是不可预期的问题?
delete this释放了类对象的内存空间,由于缓冲或者其他原因,导致这段内存空间暂时并没有被系统收回,此时这段内存是可以访问的,但是其中的值却是不确定的。获取数据成员,可能得到未初始化的随机数;访问虚函数表,指针无效的可能性非常高,造成系统崩溃。
3.如果在类的析构函数中调用delete this,会发生什么?
会导致堆栈溢出。delete的本质是“将被释放的内存调用一个或多个析构函数,然后释放内存”。显然,delete this会去调用本对象的析构函数,而析构函数中又调用delete this,形成无限递归,造成堆栈溢出,系统崩溃。