new/delete 和 malloc/free 的本质区别是什么?
new/delete 和 malloc/free 都是在 C/C++ 中用于动态内存管理的工具,但它们的本质区别在于:new/delete 是面向对象的,而 malloc/free 是面向内存的。
换句话说,malloc/free 只负责分配和释放生肉(纯粹的字节),而 new/delete 不仅分配内存,还会把生肉做成熟菜(调用构造函数初始化对象,调用析构函数清理对象)。
以下是它们的核心区别对比:
1. 本质与身份
new/delete:是 C++ 的内置运算符(Operator),受编译器控制,就像+、-一样。malloc/free:是 C/C++ 标准库中的函数(Function),包含在<cstdlib>库中。
2. 构造函数与析构函数(最核心的区别)
new:分为两步。第一步分配足够的内存,第二步自动调用该类型的构造函数来初始化对象。delete:分为两步。第一步自动调用该对象的析构函数清理资源,第二步释放内存。malloc/free:仅仅分配或释放指定字节数的内存,完全不知道“对象”的存在,绝不会调用构造和析构函数。
(如果用malloc分配带有虚函数或含有std::string等复杂成员的 C++ 类,会导致灾难性错误,因为对象没有被正确初始化。)
3. 类型安全与返回类型
new:是类型安全的。你告诉它要什么类型,它就返回什么类型的指针(例如new int返回int*),不需要强制类型转换。malloc:返回的是void*(无类型指针)。在 C++ 中,必须手动进行强制类型转换才能赋值给特定类型的指针(例如(int*)malloc(sizeof(int)))。
4. 内存大小的计算
new:编译器会自动计算需要的内存大小,你只需要提供类型(例如new MyClass)。malloc:需要你手动计算并传入需要分配的总字节数(例如malloc(sizeof(MyClass)))。
5. 分配失败时的处理
new:如果内存分配失败,默认会抛出std::bad_alloc异常。程序需要用try-catch来捕获。(也可以使用new (std::nothrow)让它返回 NULL)。malloc:如果内存分配失败,会返回NULL指针。程序员必须在每次调用后手动检查是否为 NULL。
6. 重载与自定义
new/delete:允许被重载(Overload)。你可以为特定的类重载operator new,实现自定义的内存池管理。还支持 定位 new (Placement new),允许在已经分配好的内存上构建对象。malloc/free:是标准库函数,不允许重载。
7. 内存区域
new/delete:从概念上讲,它们在 自由存储区(Free Store) 上分配内存。malloc/free:在 堆(Heap) 上分配内存。
(注:在绝大多数现代编译器的实现中,自由存储区底层就是用堆来实现的,new的底层通常也会调用malloc,但 C++ 标准在概念上将它们区分开来。)
8. 数组的处理
new/delete:有专门为数组设计的版本new[]和delete[]。new[]会记录数组的长度,并为每个元素调用构造函数;delete[]会为每个元素调用析构函数。malloc/free:只是分配一大块连续的字节,没有数组的概念。
总结图表
| 特性 | new / delete |
malloc / free |
|---|---|---|
| 属性 | C++ 运算符 (Operator) | 库函数 (Function) |
| 对象生命周期 | 调用构造/析构函数 | 不调用,只管内存 |
| 返回类型 | 具体类型的指针 (类型安全) | void* (需要强转) |
| 参数 | 指定数据类型 | 指定字节数 (sizeof) |
| 分配失败行为 | 抛出 bad_alloc 异常 |
返回 NULL |
| 是否可重载 | 可以重载 | 不可重载 |
| 配套数组操作 | new[] / delete[] |
手动算总大小,直接 free |
最佳实践与警告
- 在 C++ 中,永远优先使用
new/delete(或者更好的选择:智能指针std::unique_ptr/std::make_shared),尽量不要在 C++ 代码中出现malloc/free。 - 绝对不能混用!
- 用
new分配的内存,必须用delete释放。 - 用
malloc分配的内存,必须用free释放。 - 用
new[]分配的数组,必须用delete[]释放。 - 如果混用(例如
new出来用free释放),对于包含析构函数的类,会导致内存泄漏或未定义行为(程序崩溃)。
- 用
右滑查看面试常问