new和malloc的区别
1 语义层级不同:语言机制 vs. 库函数
new / new[] (C++ 关键字) | malloc / calloc / realloc (C 运行时函数) | |
---|---|---|
本质 | 语言级运算符;可被重载 | 库函数;无法重载 |
作用 | 分配内存 并调用构造函数 | 仅分配原始字节块,不做初始化,也不调用构造函数 |
返回类型 | 指向 请求类型 的指针;无需强制类型转换 | void* ;C++ 中需显式转换 |
失败处理 | 默认抛出 std::bad_alloc ;nothrow 版本返回 nullptr | 返回 nullptr ,并设置 errno |
释放方式 | delete / delete[] :先调用析构函数,再归还内存 | free() :直接释放字节块 |
2 对象生命周期:构造 / 析构自动 VS 手动
// new:安全地构造对象
std::string* ps = new std::string("hi"); // 调用了 std::string 的构造函数
delete ps; // 调用析构函数,再归还内存// malloc:只得到裸内存,需要手动“放置构造”(placement new)
void* raw = std::malloc(sizeof(std::string));
std::string* ps2 = new (raw) std::string("hi"); // 构造
ps2->~std::string(); // 手动析构
std::free(raw);
若忘记任何一步就会造成 未定义行为 或 内存泄漏。
3 类型与对齐保障
-
new
按目标类型所需的 最严对齐 分配;
自 C++17 起,malloc
也保证返回能满足 max-align(通常 ≥16 byte) 的地址,但老代码仍可能在自定义对齐要求(例如 SIMD 类型)下依赖new
。 -
new[]
还需在实现内部保存元素个数,以便delete[]
正确调用每个元素的析构函数——这部分管理开销是malloc
不具备的。
4 可定制性
-
重载
operator new
/operator delete
全局 或 类内 均可,用于内存池、调试填充、对齐扩展等。
例:void* MyClass::operator new(std::size_t sz) { return MyPool::allocate(sz); } void MyClass::operator delete(void* p) { MyPool::deallocate(p); }
-
malloc
不能按对象粒度定制,只能整体替换底层分配器(如 jemalloc、tcmalloc)或在使用层面改写为mymalloc()
。
5 异常安全与 RAII
new
的异常语义与 C++ 的 RAII 配合天然一致:对象构造失败时抛异常,上层 catch 后无泄漏。
使用 malloc
时必须手动检查返回值并在出错路径中 free()
已成功分配的资源。
6 何时选用哪一个?
场景 | 建议 |
---|---|
绝大多数 C++ 代码,直接需要对象 | 使用 new /delete ——保证构造/析构正确发生 |
需要与 C 库接口共享内存(libc , POSIX API, MPI 等)或操作纯字节缓冲区 | 使用 malloc /free |
自己管理对象池、序列化缓冲,不希望逐对象开销 | malloc +placement new 或者 重载 operator new |
性能敏感的大批量小对象 | 重载 operator new 或专业分配器(tcmalloc, jemalloc, std::pmr ) |
切勿交叉释放:
malloc
→delete
❌
new
→free
❌
违者均是未定义行为。
7 常见误解澄清
-
效率:
new
不必然更慢——主开销往往在底层分配算法,而不是是否调用构造函数;自定义operator new
可以比裸malloc
更快。 -
初始化为 0:只有
new int()
或new int[10]()
才会零初始化;new int
保留未定义值,与malloc
返回值同样“脏”。 -
内存来源:在大多数实现里
new
仍然最终调用某种malloc
,但附带额外的语言级步骤;标准层面它们依旧完全不同的抽象。
一句话总结
new
= 分配内存 + 构造对象 + 类型安全 + 异常语义;
malloc
= 只要一块原始字节,剩下的你自己负责。
掌握两者差异,才能在系统编程与高性能场景中游刃有余。
是的,你理解得基本正确,我来系统梳理一下这件事:
✅ new
会自动调用构造函数(创建)和析构函数(销毁)
MyClass* p = new MyClass(123); // 自动调用构造函数 MyClass(123)
...
delete p; // 自动调用析构函数 ~MyClass()
这个过程:
-
分配内存(内部可能调用
malloc
或其他分配器) -
调用构造函数,初始化对象
-
delete
会反过来先调用析构函数,再释放内存
所以用
new
分配的对象,不需要你手动构造和析构,只要new
/delete
配对即可。
❌ malloc
只分配“原始内存”,不会自动构造或析构
void* mem = malloc(sizeof(MyClass)); // 没有调用构造函数
MyClass* p = static_cast<MyClass*>(mem);
这时,p
指向一块“未初始化的内存”,你不能直接使用它:
❗你必须这样手动构造:
new (p) MyClass(123); // placement new:在已有内存上构造对象
❗用完后手动析构 + 释放内存:
p->~MyClass(); // 显式调用析构函数
free(p); // 再释放原始内存
📌 总结一下区别:
操作 | new / delete | malloc / free |
---|---|---|
内存分配 | 自动完成 | 手动 malloc(sizeof(T)) |
构造函数调用 | 自动(带参数或默认) | ❌ 不会;需手动 placement new |
析构函数调用 | 自动 | ❌ 不会;需手动 对象->~类名() |
安全性 / 易错性 | 高,配合 RAII 可防泄漏 | 容易出错,忘构造或析构都会导致 UB 或泄漏 |
✅ 示例对比(完整代码)
使用 new
(推荐方式)
MyClass* p = new MyClass(10);
// ... 使用 p
delete p;
使用 malloc
(必须小心)
void* mem = malloc(sizeof(MyClass));
MyClass* p = new (mem) MyClass(10); // placement new
// ... 使用 p
p->~MyClass(); // 手动析构
free(mem); // 手动释放内存
🔥 总结:用 malloc
管理对象时,你每次都必须手动调用构造和析构函数;
但用 new
时,这些操作都自动完成,更安全、更方便,推荐优先使用。