new/delete 和 malloc/free 区别
new/delete
和 malloc/free
最根本的区别在于:
new/delete
是 C++ 的运算符,而 malloc/free
是 C 语言的标准库函数。这个根本差异导致了它们在行为和使用上的一系列重要区别。
下面我们从多个维度进行详细对比。
核心差异总结表
特性 | new / delete | malloc() / free() |
---|---|---|
语言 | C++ 运算符 | C 库函数 (<cstdlib> ) |
内存来源 | 自由存储区 (Free Store) | 堆 (Heap) |
构造函数/析构函数 | 会调用 | 不会调用 |
返回值类型 | 返回确切类型指针 (如 MyClass* ) | 返回 void* (需要强制转换) |
分配失败行为 | 抛出 std::bad_alloc 异常 | 返回 NULL |
计算内存大小 | 编译器自动计算 | 程序员手动计算 (使用 sizeof ) |
重载 | 可以为自定义类重载 | 不可以重载 |
初始化 | 可使用 new Type() 进行值初始化 | 只能分配未初始化的内存 |
数组处理 | 有专门的 new[] 和 delete[] | 需要手动计算数组大小,统一用 free 释放 |
类型安全 | 类型安全 | 类型不安全 (需要 void* 转换) |
详细分解与示例
1. 构造与析构的调用 (最关键的差异)
这是两者最本质的区别,直接关系到 C++ 的对象生命周期管理。
new
:分配足够大小的内存。
调用类的构造函数在该内存上初始化对象。
delete
:调用类的析构函数清理对象管理的资源。
释放该对象所占用的内存。
malloc
:仅仅分配一块指定大小的原始内存,不会调用构造函数。你得到的是一个“空壳”。free
:仅仅释放之前分配的内存块,不会调用析构函数。如果对象内部管理着资源(如动态内存、文件句柄),会导致资源泄漏。
示例:
cpp
class MyClass { public:MyClass() { std::cout << "Constructor called\n"; data = new int[100]; }~MyClass() { std::cout << "Destructor called\n"; delete[] data; } private:int* data; };int main() {// 使用 new/deleteMyClass* obj1 = new MyClass; // 输出: Constructor calleddelete obj1; // 输出: Destructor called (资源被正确清理)// 使用 malloc/freeMyClass* obj2 = (MyClass*)malloc(sizeof(MyClass)); // 只分配内存,无输出// obj2 指向的内存是未初始化的,访问其成员是未定义行为free(obj2); // 只释放内存,无输出。~MyClass() 未被调用,导致 data 指向的 100 个 int 内存泄漏!return 0; }
2. 返回类型与类型安全
new
:返回的是与所分配类型严格匹配的指针(例如new int
返回int*
)。这是类型安全的。malloc
:返回的是void*
,必须由程序员进行强制类型转换才能使用。如果转换错误,编译器可能无法发现,这是类型不安全的。
示例:
cpp
// new - 类型安全 int* p1 = new int; // 正确 // double* p2 = new int; // 错误:无法从 int* 转换到 double*// malloc - 类型不安全 int* p3 = (int*)malloc(sizeof(int)); // 需要强制转换,容易写错 double* p4 = (double*)malloc(sizeof(int)); // 编译通过,但逻辑错误!运行时行为未定义。
3. 分配失败的处理
new
:在内存分配失败时,默认会抛出std::bad_alloc
异常。你可以使用std::nothrow
版本来让其返回nullptr
。cpp
try {int* p = new int[10000000000LL]; // 可能分配失败 } catch (const std::bad_alloc& e) {std::cerr << "Memory allocation failed: " << e.what() << '\n'; }// 使用 nothrow 版本 int* p = new (std::nothrow) int[10000000000LL]; if (p == nullptr) {// 处理分配失败 }
malloc
:在内存分配失败时,直接返回NULL
(或nullptr
in C++11+)。你需要检查返回值。cpp
int* p = (int*)malloc(10000000000LL * sizeof(int)); if (p == NULL) {// 处理分配失败 }
4. 内存大小计算
new
:编译器自动计算所需内存的大小。你只需要指定类型,不需要sizeof
。malloc
:程序员必须手动计算所需内存的字节数,使用sizeof
运算符。
示例:
cpp
// new - 简洁,不易错 MyClass* p1 = new MyClass; MyClass* pArray = new MyClass[10]; // 分配 10 个对象的数组// malloc - 繁琐,易错 MyClass* p2 = (MyClass*)malloc(sizeof(MyClass)); // 必须用 sizeof MyClass* pArray2 = (MyClass*)malloc(10 * sizeof(MyClass)); // 必须计算总大小 // 如果 MyClass 有自定义对齐要求,malloc 可能无法满足,而 new 可以。
5. 重载
new
/delete
:可以针对自定义类进行重载,实现自定义的内存管理策略(例如使用内存池)。malloc
/free
:不能被重载。
示例:
cpp
class MyClass { public:void* operator new(size_t size) {std::cout << "Custom new for size: " << size << '\n';return ::operator new(size); // 调用全局的 new}void operator delete(void* ptr) {std::cout << "Custom delete\n";::operator delete(ptr); // 调用全局的 delete} };int main() {MyClass* obj = new MyClass; // 输出: Custom new for size: 1delete obj; // 输出: Custom delete }
结论与建议
场景 | 推荐使用 |
---|---|
C++ 中分配单个对象 | new |
C++ 中分配对象数组 | new[] |
C++ 中释放对象 | delete |
C++ 中释放对象数组 | delete[] |
C 语言编程 | malloc / free |
分配原始内存(与 C 接口交互) | malloc / free |
实现自定义内存分配器 | 重载 operator new / operator delete |
绝对不要混用!用 new
分配的内存必须用 delete
释放;用 malloc
分配的内存必须用 free
释放;用 new[]
分配的内存必须用 delete[]
释放。混用会导致未定义行为,通常是程序崩溃。
在现代 C++ 中,为了更好的安全性和简洁性,应该优先使用智能指针(如 std::unique_ptr
, std::shared_ptr
),它们内部使用 new
和 delete
,但能自动管理生命周期,从而完全避免显式地使用 new
和 delete
。