1.C++ 动态内存分配对比:malloc/free VS new/delete以及 2.nullptr VS NULL 区别
1.C++ 动态内存分配对比
在 C++ 中,malloc/free
和 new/delete
都可以用于动态内存分配,但它们有显著的区别。以下是它们在多个维度的对比,并附带示例说明。
特性 | malloc/free | new/delete |
---|---|---|
类型安全 | ❌ 需要显式类型转换 | ✅ 类型安全,自动推导类型 |
构造/析构函数 | ❌ 不会调用构造或析构函数 | ✅ 会调用构造函数和析构函数 |
返回值类型 | void* ,需要强转 | 返回具体类型指针,无需转换 |
错误处理机制 | 分配失败返回 nullptr | 默认抛出 std::bad_alloc ,可用 nothrow 方式改为返回 nullptr |
多态支持 | ❌ 无多态性,释放对象不会触发虚析构 | ✅ 支持多态,虚析构函数可正确调用 |
内存释放方式 | free(p) | delete p (或 delete[] p ) |
数组分配支持 | ❌ 需要手动计算内存大小 | ✅ 使用 new Type[n] 分配,自动构造 n 个对象 |
内存分配与释放对应性 | 必须使用 free 释放由 malloc 分配的内存 | 必须使用 delete 释放由 new 分配的内存 |
1. 类型安全
malloc/free:
需要显式类型转换,不具备类型安全。
int* p = (int*)malloc(sizeof(int)); // 需要强制类型转换
new/delete:
类型安全,自动推导类型。
int* p = new int; // 类型自动推导
2. 构造与析构函数调用
malloc/free:
不会调用构造函数或析构函数。
class Test {
public:Test() { std::cout << "构造函数\n"; }~Test() { std::cout << "析构函数\n"; }
};Test* t = (Test*)malloc(sizeof(Test)); // 不会输出任何信息
free(t); // 也不会调用析构函数
new/delete:
自动调用构造函数和析构函数。
Test* t = new Test(); // 输出:构造函数
delete t; // 输出:析构函数
3. 返回值类型
malloc/free:
返回 void*
,需要强制转换。
int* p = (int*)malloc(sizeof(int));
new/delete:
返回具体类型指针,无需转换。
int* p = new int;
4. 错误处理机制
malloc/free:
分配失败返回 nullptr
,需要手动判断。
int* p = (int*)malloc(sizeof(int));
if (!p) {// 处理错误
}
new/delete:
默认抛出 std::bad_alloc
异常,但可通过 nothrow
返回 nullptr
。
try {int* p = new int;
} catch (std::bad_alloc& e) {std::cout << "分配失败: " << e.what() << "\n";
}int* p = new (std::nothrow) int;
if (!p) {// 分配失败
}
5. 多态支持
malloc/free:
不支持多态,虚析构函数不会调用。
class Base {
public:virtual ~Base() { std::cout << "Base析构\n"; }
};
class Derived : public Base {
public:~Derived() { std::cout << "Derived析构\n"; }
};Base* obj = (Base*)malloc(sizeof(Derived));
free(obj); // 不调用析构函数
new/delete:
支持多态,虚析构函数能正常调用。
Base* obj = new Derived();
delete obj; // 正确调用 Derived 和 Base 的析构函数
6. 内存释放方式
malloc/free:
使用 free(p)
。
int* p = (int*)malloc(sizeof(int));
free(p);
new/delete:
使用 delete p
或 delete[] p
。
int* p = new int;
delete p;
7. 数组分配支持
malloc/free:
手动计算大小,无构造函数支持。
int* arr = (int*)malloc(sizeof(int) * 10);
free(arr);
new/delete:
使用 new Type[n]
,支持自动构造每个对象。
int* arr = new int[10];
delete[] arr;
8. 内存分配与释放必须对应
malloc/free:
必须配对使用。
int* p = (int*)malloc(sizeof(int));
free(p); // OK
// delete p; // 错误!
new/delete:
也必须配对使用。
int* p = new int;
delete p; // OK
// free(p); // 错误!
2nullptr
vs NULL
nullptr
和 NULL
都表示“空指针”,但它们的底层机制、类型安全性和推荐用法在 C++ 中是有差别的,尤其是在现代 C++(C++11 以后)中:
✅ 简明对比:nullptr
vs NULL
比较点 | nullptr | NULL |
---|---|---|
所在语言 | 只在 C++ 中(从 C++11 起) | C 和 C++ 都有 |
类型 | std::nullptr_t | 通常是 0 或 (void*)0 |
类型安全 | ✅ 类型安全(不会误配) | ❌ 可能引起重载歧义 |
推荐程度 | ✅ 强烈推荐在 C++ 中使用 | 仅建议在 C 中使用 |
兼容函数重载 | ✅ 正确调用指针版本 | ❌ 可能调用错误的重载版本 |
📌 示例:看出区别的关键例子
🎯 示例 1:重载歧义
void f(int);
void f(char*);f(NULL); // ⚠️ 可能调用 f(int),不是你想要的!
f(nullptr); // ✅ 一定调用 f(char*),因为类型安全
NULL
是个整数常量(通常是0
),所以可能匹配到int
版本。nullptr
是一个专门的空指针类型,不可能匹配int
,只能匹配指针。
🎯 示例 2:类型检查
int* p = nullptr; // ✅ OK
int* q = NULL; // ⚠️ 也行,但是隐式从 int 转指针
✅ 总结:什么时候用哪个?
场景 | 推荐用法 |
---|---|
写 C++ 代码 | nullptr ✅ |
写 C 代码 | NULL ✅ |
写 C++ 98/03 | NULL (但注意类型风险) |