C++ 分配内存 new/malloc 区别
C++ 分配内存 new/malloc 区别
- 1. new/malloc 设计目的
- 2. new/malloc 内存分配与释放机制
- 3. new/malloc 应用场景对比
- 3.1 new/delete典型应用场景
- 场景 1:动态创建类对象
- 场景 2:动态创建多态对象(基类指针指向派生类)
- 场景 3:创建带资源的对象(RAII)
- 场景 4:自定义内存管理(重载 operator new/delete)
- 场景 5:配合智能指针使用(推荐现代 C++)
- 3.2 malloc/free典型应用场景
- 场景 1:与 C 库或系统 API 交互
- 场景 2:分配原始字节缓冲区
- 场景 3:C 风格结构体数组或 POD 类型(Plain Old Data)
- 场景 4:自定义内存池 / 分配器底层实现
- 场景 5:与 C 语言共享数据
- 4. 现代 C++ 替代方案
1. new/malloc 设计目的
new/malloc 简介
🔹new/delete 管“对象”;需要构造/析构;
🔹malloc/free 管“内存”;按字节分配和释放内存块,而不是对象
new/malloc 设计目的
项目 | new/delete | malloc/free |
---|---|---|
所属语言 | C++ 关键字(语言内建) | C 标准库函数(<cstdlib> ) |
设计目的 | 面向对象内存管理 —— 创建/销毁“对象” | 原始内存管理 —— 分配/释放“字节块” |
核心理念 | 调用构造/析构函数、类型安全、异常安全 | 仅操作内存,不关心类型或对象生命周期 |
是否可重载 | ✅ 可重载 operator new/delete | ❌ 不可 |
2. new/malloc 内存分配与释放机制
特性 | new/delete | malloc/free |
---|---|---|
分配方式 | 在堆上为对象分配内存 | 在堆上分配指定字节的内存 |
调用构造函数 | ✅ 自动调用 | ❌ 不会调用 |
调用析构函数 | ✅ delete 自动调用 | ❌ 不会调用 |
返回类型 | 对象的类型指针(自动转换) | void* (需手动强制类型转换) |
内存不足时 | 抛出 std::bad_alloc 异常 | 返回 nullptr |
释放方式 | delete 或 delete[] | free |
可混用 | ❌ 严禁混用 | ❌ 严禁混用 |
📘 示例对比:
// new/delete
MyClass* obj = new MyClass(10);
delete obj;// malloc/free
MyClass* obj2 = (MyClass*)malloc(sizeof(MyClass));
// ⚠️ 构造函数不会被调用
free(obj2);
3. new/malloc 应用场景对比
场景类型 | 推荐方式 | 理由 |
---|---|---|
动态创建 C++ 对象 | new/delete | 自动调用构造与析构 |
复杂类、多态体系结构 | new/delete | 保证虚析构安全 |
与 C 接口交互 | malloc/free | C 接口不识别 C++ 对象语义 |
原始字节缓冲区(图像、网络包) | malloc/free | 不需要构造函数,灵活 |
C 风格结构体数组(POD 类型) | malloc/free | 无对象语义即可 |
RAII / 智能指针管理对象 | std::make_unique / std::make_shared | 安全、自动释放 |
内存池 / 分配器实现 | malloc/free 或重载 operator new | 可控、高性能 |
STL 容器管理对象 | 容器内部 new/delete 自动管理 | 不需手动分配 |
3.1 new/delete典型应用场景
场景 1:动态创建类对象
当对象大小或数量在运行时才确定时,用 new 创建。
// 编译时不知道要多少个点
int n;
std::cin >> n;
Point* points = new Point[n]; // 调用 n 次构造函数
// ...
delete[] points; // 调用 n 次析构函数
📌 特点:
- 支持任意复杂构造(含参数)。
- 自动调用析构函数释放资源。
- 推荐用在动态生命周期对象。
场景 2:动态创建多态对象(基类指针指向派生类)
new 能确保正确调用构造/析构链,是面向对象编程的常见做法。
Base* obj = new Derived(); // 调用 Derived 构造
// ...
delete obj; // 调用 Derived + Base 析构
📌 关键点:
- 析构函数必须是 虚函数(virtual ~Base())。
- 用 malloc 无法触发这些行为,会造成资源泄露或未定义行为。
场景 3:创建带资源的对象(RAII)
new 是 RAII 模式的基础:对象管理资源,生命周期自动控制。
std::ofstream* f = new std::ofstream("data.txt");
// 析构时自动关闭文件
delete f;
📘 RAII(Resource Acquisition Is Initialization)强调:
- 构造 → 获取资源,析构 → 释放资源。
- malloc 不能实现这种模式。
场景 4:自定义内存管理(重载 operator new/delete)
在性能关键或嵌入式系统中,可能要控制内存分配策略。
void* MyClass::operator new(size_t size) {return MyMemoryPool::Allocate(size);
}
void MyClass::operator delete(void* p) {MyMemoryPool::Free(p);
}
📌 可用于:
- 内存池 / 对象池;
- 高性能游戏引擎;
- 实时系统。
场景 5:配合智能指针使用(推荐现代 C++)
现代 C++ 几乎不直接 delete 对象,而是用智能指针管理。
auto p = std::make_unique<MyClass>(); // 内部调用 new
auto q = std::make_shared<MyClass>(); // 自动引用计数
📘 优点:
- 自动释放;
- 避免内存泄漏;
- 异常安全。
3.2 malloc/free典型应用场景
场景 1:与 C 库或系统 API 交互
如果你在 C++ 程序中调用 纯 C 接口,或者 系统 API(如 Windows / POSIX 函数),这些接口通常要求使用 malloc/free。
// C 库函数接口
void* buffer = malloc(1024);
read(fd, buffer, 1024);
process_data((unsigned char*)buffer);
free(buffer);
📘 原因:
C 接口不知道 C++ 的 new 机制,也不会调用构造/析构。使用 malloc/free 可保证兼容性。
场景 2:分配原始字节缓冲区
当你只需要“字节空间”,不需要“对象语义”时,malloc 非常合适。
void* data = malloc(width * height * 3); // 分配图像缓冲区
memset(data, 0, width * height * 3);
free(data);
📌 典型用途:
- 图像、音频、网络包、二进制数据缓冲;
- 临时缓存;
- 原始内存池。
场景 3:C 风格结构体数组或 POD 类型(Plain Old Data)
对于无构造函数/析构函数的简单结构体,malloc 足够用。
struct Point { float x, y, z; };Point* pts = (Point*)malloc(100 * sizeof(Point));
for (int i = 0; i < 100; i++) {pts[i].x = pts[i].y = pts[i].z = 0;
}
free(pts);
📘 注意:
如果结构体有构造函数(哪怕是隐式的),必须用 new,否则构造函数不会执行。
场景 4:自定义内存池 / 分配器底层实现
即使在现代 C++ 中,很多高性能容器(如 std::pmr、游戏引擎、数据库缓存)底层仍使用 malloc/free 或 aligned_alloc 实现。
void* block = malloc(blockSize);
MemoryPool::Add(block);
📘 用途:
- 控制分配策略;
- 避免频繁系统调用;
- 做对象池、缓冲池、环形队列等。
场景 5:与 C 语言共享数据
当 C++ 模块需要和 C 模块共享数据结构时,内存必须由 C 可识别的方式分配。
extern "C" void c_function(void* buf);void* buf = malloc(256);
c_function(buf);
free(buf);
4. 现代 C++ 替代方案
目标 | 推荐替代 | 原因 |
---|---|---|
动态数组 | std::vector<T> | 自动释放,支持对象语义 |
单个堆对象 | std::unique_ptr<T> / std::make_unique<T> | 无需手动 delete |
引用计数对象 | std::shared_ptr<T> / std::make_shared<T> | 自动管理生命周期 |
临时缓冲区 | std::vector<uint8_t> / std::byte | 安全且类型明确 |
多线程内存管理 | 自定义内存池 + operator new 重载 | 控制分配策略与性能 |