C 的 malloc/free 与 C++ 的 new/delete 一些区别

C 的 malloc/free 与 C++ 的 new/delete 到底差在哪?一文彻底说清

很多从 C 过渡到 C++ 的程序员,最开始都习惯用 mallocfree 来管理动态内存。到了 C++,又多出了 newdelete,看起来好像就是换了个写法。但在面试中,这个问题一深挖就能筛掉一大半人。

今天我们就从语法、语义、底层机制三个层面,把它们的区别彻底拆解清楚。

1. 本质区别:函数 vs 运算符

这是最根本的差异:

  • malloc/free 是 C 标准库提供的函数
  • new/delete 是 C++ 语言内置的运算符

因为是运算符,newdelete 可以被重载 ,你可以为特定类自定义内存分配策略(对象池、内存池等)。而 malloc/free 只是函数,你只能通过不同实现来替换(如 jemalloc、tcmalloc),无法针对某个类做特殊处理。

cpp 复制代码
// new 可以重载
class MyClass {
public:
    static void* operator new(size_t size) {
        std::cout << "Custom new for MyClass\n";
        return malloc(size);
    }
    static void operator delete(void* ptr) {
        std::cout << "Custom delete for MyClass\n";
        free(ptr);
    }
};

2. 构造函数与析构函数:最大的语义鸿沟

这是面试中最核心的考点,也是最容易被问到的地方。

new 会调用构造函数,malloc 不会

cpp 复制代码
class Object {
public:
    Object() { std::cout << "Constructor called\n"; }
    ~Object() { std::cout << "Destructor called\n"; }
    int data[100];
};

// 使用 new
Object* p1 = new Object;  // 输出:Constructor called
                          // 同时分配了足够的内存,并构造了对象

// 使用 malloc
Object* p2 = (Object*)malloc(sizeof(Object)); 
// 什么都没输出,只是拿了一块原始内存
// p2 指向的只是一堆字节,不是真正的 Object 对象!

delete 会调用析构函数,free 不会

cpp 复制代码
delete p1;  // 输出:Destructor called,然后释放内存
free(p2);   // 直接释放内存,不调用析构函数

关键结论new/delete 做了两件事:内存分配/释放 + 对象构造/析构 。而 malloc/free 只做内存分配/释放。

必须配对使用,混用是未定义行为

cpp 复制代码
Object* p = (Object*)malloc(sizeof(Object));
delete p;  // 危险!delete 会尝试调用析构函数,但对象根本没构造过

反过来也一样:

cpp 复制代码
Object* p = new Object;
free(p);  // 危险!析构函数不会被调用,可能造成资源泄漏

3. 返回类型与类型安全

malloc 返回 void*,需要手动强制类型转换:

cpp 复制代码
int* p = (int*)malloc(sizeof(int) * 10);

new 直接返回正确的类型指针,类型安全且代码更简洁:

cpp 复制代码
int* p = new int[10];  // 不需要转换

4. 内存分配失败时的行为

malloc 分配失败返回 NULL(或 nullptr

cpp 复制代码
int* p = (int*)malloc(SIZE_MAX);
if (p == NULL) {
    // 手动处理失败
}

new 分配失败默认抛出 std::bad_alloc 异常

cpp 复制代码
try {
    int* p = new int[SIZE_MAX];
} catch (const std::bad_alloc& e) {
    // 处理异常
}

C++ 也提供了不抛异常的版本:

cpp 复制代码
int* p = new(std::nothrow) int[SIZE_MAX];
if (p == nullptr) {
    // p 为空,和 malloc 行为类似
}

5. 计算所需内存大小的方式

malloc 需要手动计算字节数

cpp 复制代码
int* arr = (int*)malloc(sizeof(int) * n);

new编译器自动计算

cpp 复制代码
int* arr = new int[n];  // 编译器知道 int 的大小

不仅代码更简洁,还避免了手动计算时可能出现的错误(比如忘记乘 sizeof)。

6. new/delete 的数组版本

newdelete 有单独的数组形式:

cpp 复制代码
// 单个对象
int* p1 = new int;
delete p1;

// 数组
int* p2 = new int[10];
delete[] p2;  // 必须用 delete[]

绝对不能混用

cpp 复制代码
int* p = new int[10];
delete p;  // 未定义行为!可能只调用一次析构函数,甚至导致内存泄漏

为什么必须配对?因为当数组元素是类对象时,delete[] 需要知道有多少个对象,依次调用析构函数。编译器通常在数组内存块前面额外存放一个记录元素个数的值,delete[] 会去读取它,而 delete 不会。

7. 重新分配内存

malloc 有配套的 realloc,可以原地调整已分配内存块的大小:

cpp 复制代码
int* p = (int*)malloc(sizeof(int) * 10);
p = (int*)realloc(p, sizeof(int) * 20);  // 扩容到 20 个 int

new/delete 没有配套的 realloc 。如果想扩容,只能手动 new 一块更大的内存,拷贝内容,再 delete 旧内存。这也是为什么 std::vector 在扩容时需要重新分配并移动/拷贝元素。

8. 面试常考清单

这里整理一下面试中的高频问题:

8.1 new 和 malloc 的根本区别是什么?

答案要点

  1. new 是运算符,malloc 是函数。
  2. new 会调用构造函数,malloc 不会。
  3. new 返回带类型的指针,malloc 返回 void*
  4. new 失败抛异常,malloc 失败返回 NULL。
  5. new 可由编译器计算大小,malloc 需手动指定。

8.2 delete p 和 delete[] p 有什么区别?为什么必须配对?

答案要点

  • delete 用于单个对象,调用一次析构函数。
  • delete[] 用于数组,会读取数组长度信息,依次调用每个元素的析构函数。
  • 混用是未定义行为,可能导致内存泄漏或程序崩溃。

8.3 可以用 free 释放 new 出来的内存吗?反过来呢?

答案要点 :绝对不能。new 分配的内存必须用 delete 释放(反之亦然),否则会导致构造函数/析构函数未被正确调用,是未定义行为。

8.4 placement new 是什么?

答案要点:在已分配的内存上构造对象,不分配新内存。

cpp 复制代码
void* buffer = malloc(sizeof(Object));
Object* obj = new(buffer) Object();  // 在 buffer 上构造 Object
obj->~Object();  // 手动调用析构函数(placement new 没有对应的 delete)
free(buffer);

常用于内存池、嵌入式系统等场景,也是 std::vector 等容器底层实现的一部分。

8.5 如何让 new 在分配失败时不抛异常?

答案要点 :使用 std::nothrow 版本。

cpp 复制代码
int* p = new(std::nothrow) int[100];
if (p == nullptr) { /* 处理失败 */ }

9. 何时用 malloc,何时用 new?

在 C++ 中,默认应该使用 new/delete。它们与 C++ 的对象模型完美集成,能正确处理构造和析构。

少数场景下你可能仍需要 malloc/free

  • 与 C 代码交互,必须使用 C 接口分配和释放内存。
  • 需要 realloc 的原地扩容特性。
  • 实现自定义 operator new 时,底层通常还是调用 malloc

理解这些区别,不仅是为了应付面试,更是为了写出安全、正确的 C++ 代码。在 C++ 的世界里,请优先把内存管理交给 new/delete,或者更好的是,交给智能指针和容器。

相关推荐
Chen_harmony1 小时前
十九、数据在内存中的存储
c语言·开发语言
mmz12071 小时前
广搜题目练习(c++)
c++·算法
iiiiyu1 小时前
⾯向对象和集合编程题
java·大数据·开发语言·数据结构·编程语言
郝学胜-神的一滴1 小时前
Qt 高级开发 006: 架构全解 + 高效学习指南
开发语言·c++·qt·程序人生·架构
爱编码的小八嘎1 小时前
MFC深入-消息映射的实现
c语言
Achou.Wang1 小时前
Concurrency patterns - Go 并发模式
开发语言·后端·golang
存在morning1 小时前
【GO语言开发实践】三 GO 工程化快速上手
开发语言·后端·golang
雁迟1 小时前
第七章:R 向量用法(最核心数据结构)
开发语言·数据结构·r语言
Achou.Wang1 小时前
Go语言并发编程中的死锁防范与破解之道
服务器·开发语言·golang