new和malloc的区别

1 语义层级不同:语言机制 vs. 库函数

new / new[] (C++ 关键字)malloc / calloc / realloc (C 运行时函数)
本质语言级运算符;可被重载库函数;无法重载
作用分配内存 并调用构造函数仅分配原始字节块,不做初始化,也不调用构造函数
返回类型指向 请求类型 的指针;无需强制类型转换void*;C++ 中需显式转换
失败处理默认抛出 std::bad_allocnothrow 版本返回 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

切勿交叉释放

  • mallocdelete

  • newfree
    违者均是未定义行为。


7 常见误解澄清

  1. 效率new 不必然更慢——主开销往往在底层分配算法,而不是是否调用构造函数;自定义 operator new 可以比裸 malloc 更快。

  2. 初始化为 0:只有 new int()new int[10]() 才会零初始化;new int 保留未定义值,与 malloc 返回值同样“脏”。

  3. 内存来源:在大多数实现里 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 / deletemalloc / 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 时,这些操作都自动完成,更安全、更方便,推荐优先使用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值