📌 相关专栏
- 【Linux专栏】
- 【C语言专栏】
- 【测试专栏】
- 【MySQL专栏】
- 【C++ 专栏】
📌 相关文章推荐
- 【C+++】C++类和对象5:一文搞懂C++类型转换、初始化列表与static成员
- 【C++】C++类与对象4: 一文吃透C++友元、内部类、匿名对象与编译优化要点
- 【C++】C++核心语法:函数重载与缺省参数原理与避坑
- 【C语言】超实用VS调试干货,新手也能轻松排错
很高兴你点开这篇文章✨
这里会持续更新我喜欢的内容,关注我,一起慢慢变好呀
👍 点赞 ⭐ 收藏 💬 评论
文章目录
- 前言
- 一、new/delete的基本用法
-
- 1.1 单个对象的分配与释放
- 1.2 带初始化的分配
- 1.3 数组的分配与释放
- 二、new/delete vs malloc/free
-
- 2.1 核心区别
- 2.2 最重要的区别:构造/析构
- 2.3 不要混用
- 三、new和delete的底层原理
-
- 3.1 new的工作流程
- 3.2 delete的工作流程
- 四、自定义类型的数组分配
-
- 4.1 new[ ]和delete[ ]必须匹配
- 4.2 数组初始化方式
- 五、内存分配失败处理
-
- 5.1 new的失败处理机制
- 5.2 循环分配直到失败
- 5.3 不抛异常的new(nothrow)
- 六、定位new表达式(placement new)
- 七、常见错误与最佳实践
-
- 7.1 new和delete不匹配
- 7.2 忘记释放内存(内存泄漏)
- 7.3 重复释放(double free)
- 7.4 释放后继续使用(野指针)
- 八、链表节点示例
- 九、知识点汇总
- 十、常见面试题
前言
在C语言中,我们用malloc和free来管理动态内存。到了C++,虽然兼容C的方式,但引入了更强大的new和delete操作符。
🐾 这一篇我们来学习:
- new和delete的基本用法
- 为什么C++要引入新的内存管理方式
- new[]和delete[]的正确匹配
- 内存分配失败怎么办
- 常见错误和最佳实践
🐶 🐾 ✨ 🐾 🐶
一、new/delete的基本用法
1.1 单个对象的分配与释放
// C语言方式
int* p1 = (int*)malloc(sizeof(int));
free(p1);
// C++方式
int* p2 = new int; // 分配一个int大小的内存
delete p2; // 释放内存
1.2 带初始化的分配
// 分配并初始化
int* p3 = new int(0); // 初始化为0
int* p4 = new int(10); // 初始化为10
int* p5 = new int{20}; // C++11统一初始化
delete p3;
delete p4;
delete p5;
1.3 数组的分配与释放
// 分配数组
int* arr1 = new int[10]; // 10个未初始化的int
int* arr2 = new int[10]{0}; // 10个初始化为0的int
int* arr3 = new int[10]{1,2,3,4,5}; // 前5个指定值,后5个初始化为0
// 释放数组(注意:必须用delete[])
delete[] arr1;
delete[] arr2;
delete[] arr3;
🐶 🐾 ✨ 🐾 🐶
二、new/delete vs malloc/free
2.1 核心区别
| 语言 | C语言函数 | C++操作符 |
| 类型安全 | 需要强转(void*) | 类型安全,无需强转 |
| 内存大小 | 手动计算sizeof | 编译器自动计算 |
| 构造函数 | 不调用 | 自动调用 |
| 析构函数 | 不调用 | 自动调用 |
| 失败返回 | NULL | 抛出bad_alloc异常 |
2.2 最重要的区别:构造/析构
class A
{
public:
A(int a1 = 0, int a2 = 0) : _a1(a1), _a2(a2)
{
cout << "A(int a1 = 0, int a2 = 0)" << endl;
}
~A()
{
cout << "~A()" << endl;
}
private:
int _a1 = 1;
int _a2 = 1;
};
int main()
{
// malloc只会分配内存,不会调用构造函数
A* p1 = (A*)malloc(sizeof(A)); // 无输出
// new会分配内存并调用构造函数
A* p2 = new A; // 输出:A(int a1 = 0, int a2 = 0)
A* p3 = new A(1, 2); // 输出:A(int a1 = 0, int a2 = 0)
free(p1); // 不会调用析构函数
delete p2; // 调用析构函数后释放内存
delete p3;
return 0;
}
2.3 不要混用
绝对禁止混用malloc/free和new/delete:
int main()
{
int* p1 = new int;
free(p1); // ❌ 未定义行为
int* p2 = (int*)malloc(sizeof(int));
delete p2; // ❌ 未定义行为
A* p3 = new A;
free(p3); // ❌ 不会调用析构函数,可能导致资源泄漏
A* p4 = (A*)malloc(sizeof(A));
delete p4; // ❌ 未定义行为
return 0;
}
🐶 🐾 ✨ 🐾 🐶
三、new和delete的底层原理
3.1 new的工作流程
A* p = new A(1, 2);
等价于:
1. 调用operator new函数分配原始内存(底层调用malloc) 2. 在分配的内存上调用A的构造函数 3. 返回指向对象的指针
3.2 delete的工作流程
delete p;
等价于:
1. 调用p指向对象的析构函数 2. 调用operator delete函数释放内存(底层调用free)
四、自定义类型的数组分配
4.1 new[ ]和delete[ ]必须匹配
int main()
{
// 正确匹配
A* p1 = new A[5];
delete[] p1; // ✅ 调用5次析构函数
// 错误匹配
A* p2 = new A[5];
delete p2; // ❌ 只调用1次析构函数,可能导致内存泄漏
B* p3 = new B[10];
delete p3; // ❌ 可能崩溃或未定义行为
return 0;
}
🐶 🐾 ✨ 🐾 🐶
4.2 数组初始化方式
int main()
{
// 方式1:先用有名对象初始化
A aa1(1, 1);
A aa2(2, 2);
A aa3(3, 3);
A* p1 = new A[3]{aa1, aa2, aa3};
// 方式2:用匿名对象初始化
A* p2 = new A[3]{ A(1,1), A(2,2), A(3,3) };
// 方式3:C++11统一初始化
A* p3 = new A[3]{ {1,1}, {2,2}, {3,3} };
delete[] p1;
delete[] p2;
delete[] p3;
return 0;
}
🐶 🐾 ✨ 🐾 🐶
五、内存分配失败处理
5.1 new的失败处理机制
- C语言中malloc失败返回NULL,需要手动判断:
int* p = (int*)malloc(1024 * 1024 * 1024);
if (p == NULL) {
// 处理内存不足
}
- C++的new失败时抛出异常,而不是返回NULL:
int main()
{
try
{
// 尝试分配1GB内存
void* p1 = new char[1024 * 1024 * 1024];
cout << p1 << endl;
void* p2 = new char[1024 * 1024 * 1024];
cout << p2 << endl;
void* p3 = new char[1024 * 1024 * 1024];
cout << p3 << endl;
}
catch (const exception& e)
{
cout << e.what() << endl; // 输出:bad allocation
}
return 0;
}
5.2 循环分配直到失败
void func()
{
int n = 1;
while (1)
{
void* p1 = new char[1024 * 1024]; // 每次分配1MB
cout << p1 << "->" << n << endl;
++n;
}
}
int main()
{
try
{
func();
}
catch (const exception& e)
{
cout << e.what() << endl; // 最终会抛出bad_alloc
}
return 0;
}
5.3 不抛异常的new(nothrow)
如果不想用异常,可以使用nothrow版本:
#include <new> // 需要包含这个头文件
int main()
{
int* p = new (nothrow) int[1024 * 1024 * 1024];
if (p == nullptr)
{
cout << "内存分配失败" << endl;
}
delete[] p;
return 0;
}
🐶 🐾 ✨ 🐾 🐶
六、定位new表达式(placement new)
定位new可以在已分配的内存上构造对象:
#include <new> // 需要包含这个头文件
int main()
{
// 分配原始内存
void* p = malloc(sizeof(A));
// 在已分配的内存上构造A对象
A* a = new(p) A(1, 2);
// 使用对象
a->Print();
// 手动调用析构函数
a->~A();
// 释放原始内存
free(p);
return 0;
}
🐾 使用场景: 内存池、自定义内存管理、嵌入式系统。
🐶 🐾 ✨ 🐾 🐶
七、常见错误与最佳实践
7.1 new和delete不匹配
| new | delete |
| new[ ] | delete[ ] |
| malloc | free |
// ❌ 错误示例
A* p1 = new A;
delete[] p1; // 错误!
A* p2 = new A[10];
delete p2; // 错误!只调用一次析构
// ✅ 正确示例
A* p1 = new A;
delete p1;
A* p2 = new A[10];
delete[] p2;
7.2 忘记释放内存(内存泄漏)
// ❌ 内存泄漏
void func()
{
int* p = new int[100];
// 忘记delete[] p
}
// ✅ 正确做法
void func()
{
int* p = new int[100];
// … 使用p
delete[] p;
}
7.3 重复释放(double free)
int* p = new int;
delete p;
delete p; // ❌ 未定义行为,可能崩溃
7.4 释放后继续使用(野指针)
int* p = new int(10);
delete p;
*p = 20; // ❌ 未定义行为,p已经是野指针
// ✅ 释放后置空
delete p;
p = nullptr;
🐶 🐾 ✨ 🐾 🐶
八、链表节点示例
struct ListNode
{
int val;
ListNode* next;
ListNode(int x)
: val(x)
, next(nullptr)
{}
};
int main()
{
// 创建链表:1->2->3->4
ListNode* n1 = new ListNode(1);
ListNode* n2 = new ListNode(2);
ListNode* n3 = new ListNode(3);
ListNode* n4 = new ListNode(4);
n1->next = n2;
n2->next = n3;
n3->next = n4;
// 遍历链表
ListNode* cur = n1;
while (cur)
{
cout << cur->val << " ";
cur = cur->next;
}
cout << endl;
// 释放链表内存
cur = n1;
while (cur)
{
ListNode* next = cur->next;
delete cur;
cur = next;
}
return 0;
}
🐶 🐾 ✨ 🐾 🐶
九、知识点汇总
| new | 分配内存 + 调用构造函数 |
| delete | 调用析构函数 + 释放内存 |
| new[ ] | 分配数组内存 + 依次调用构造 |
| delete[ ] | 依次调用析构 + 释放数组内存 |
| malloc/free | 只分配/释放内存,不调用构造/析构 |
| 匹配规则 | new配delete,new[]配delete[] |
| 失败处理 | 抛bad_alloc异常(可用nothrow) |
| 定位new | 在已分配内存上构造对象 |
🐶 🐾 ✨ 🐾 🐶
十、常见面试题
🐾 Q1:new和malloc的区别?
- 1. new是操作符,malloc是函数
- 2. new会调用构造函数,malloc不会
- 3. new返回类型安全的指针,malloc返回void*
- 4. new失败抛异常,malloc失败返回NULL
- 5. new不需要手动计算大小,malloc需要sizeof
🐾 Q2:delete和free的区别?
- 1. delete会调用析构函数,free不会
- 2. delete是操作符,free是函数
🐾 Q3:new[]和delete[]不匹配会怎样?
- 未定义行为。可能内存泄漏、程序崩溃、只调用一次析构函数。
🐾 Q4:如何避免内存泄漏?
- 1. 确保new和delete成对出现
- 2. 使用智能指针(后续课程)
- 3. 遵循RAII原则
下一篇我们来学习:
- 模板原理
- 实例化
- 特化
🐶 🐾 ✨ 🐾 🐶
谢谢你看到这里呀
如果喜欢这篇内容,点个关注,下次更新不迷路✨
👍 点赞 ⭐ 收藏 💬 评论



