【C++】——new和delete与malloc和free的区别
【C++】——new和delete与malloc和free的区别
C语言提供了malloc和free用于开辟和释放内存,C++提供new和delete进行内存操作,本文将new和delete与malloc free做对比,便于理解和巩固知识。还讲解了为什么delete和delete[]不能混用
new
讲解new和malloc的区别,以及new的底层。
与malloc区别
-
malloc
是函数,new
是操作符,也是关键字。 -
malloc
返回void*
类型指针,new
直接返回对象。 -
malloc
只负责开辟空间,不负责对对象初始化,new
除了开辟空间、还负责调用对象的构造函数完成初始化。 -
malloc
需要传参开辟字节数,new
传参对象类型和对象个数,由底层完成开辟空间的字节数的计算。 -
malloc
开辟空间失败会返回NULL
,new
开辟空间失败会抛异常(抛异常符合现代编程风格,也方便和try catch
组合使用)。
底层实现
简介new的底层原理,为什么new不仅可以完成开辟空间、还可以完成初始化。(关于为什么delete[]和delete不能混用的部分在delete讲解
new
的底层是operator new
和placement new
, operator new
封装了malloc
来开辟空间,placement new
用完成构造函数的调用。以下代码使用底层方法完成new
的功能:
// 对单个自定义类型对象new
// MyClass* obj = new MyClass(args); // 使用 new 创建空间
// 本质上
MyClass* obj = (MyClass*)operator new(sizeof(MyClass)); //使用operator new申请空间
new(obj)MyClass(args); // 使用placement new调用MyClass的构造函数,对空间初始化.后面的()用于构造函数传参,不传参默认调用默认构造函数// 对数组new
// MyClass* obj = new MyClass[size]{args); // 使用 new 创建空间
MyClass* arrayMemory = (MyClass*)operator new[](sizeof(MyClass) * size);
// 使用placement new[]构造数组
new(arrayMemory) MyClass[size]{MyClass(1, "对象1"),MyClass(2, "对象2"),MyClass(3, "对象3")
};
-
operator new
:通过代码可以发现,operator new
和malloc
使用方法一样,不同的是,new
封装了operator new
,所以你可以通过重载operator new
实现特定的类/全局,在自己规定的内存分配器中开辟空间。而operator new
本质上就是malloc
的封装 -
placement new
:完成构造函数的调用。
delete
讲解delete和free的区别,一起为什么delete和delete[]不能混用。
和free的区别
free
是函数,delete
是操作符,也是关键字。free
只负责释放空间,不负责对对象调用析构函数,delete
除了负责释放空间,还负责对对象调用析构函数。free
需要传参释放空间首部的指针起始位置,delete
传参对象类型和对象个数,由底层完成调用析构函数的次数的计算。
底层实现
delete
封装了operator delete
和desturction(对应对象的析构函数)
,operator delete
则是封装了free_dbge_
来释放空间。
在使用delete
时,本质是先调用析构函数,再释放空间。
为什么delete[]
和new[]
搭配使用,delete
和new
搭配使用?
为了在使用delete时,编译器知道应该调用多少次析构函数,new
开辟空间时,会在返回的指针前另外开辟size_t
空间存储对象数目。
另外,如果不需要记录,编译器自然也不会开辟空间记录。比如内置类型、没有析构函数的情况下。
图解:
通过查看内存验证:
对于有析构函数的类的验证:
对于没有析构函数或者内置类型的验证:
关于new
的小tips
另外,我们试一下如果删除了析构函数再去new
一个数组出来会出现什么情况。
class A
{
public:A(){ c = 1;}~A() = delete; // 🚨 问题在这里!int c;
};
int main()
{A* p = new A[10];// 如果第5个对象构造时抛出异常,前4个需要析构// 如果整个new表达式失败,所有已构造对象需要析构return 0;
}
编译出错,这是为什么呢?
当你使用 new A[10]
时,编译器需要:
- 构造10个A对象
- 如果分配失败或数组生命周期结束,需要析构已构造的对象
由于 ~A() = delete
,编译器无法生成析构数组元素的代码。