小学生学C++编程语法知识(C++申请内存与释放内存)
new、delete、new[]、delete[]、allocator都在解决一个问题:
“我向计算机借了一块内存,用完以后应该怎样把它还回去?”
一、先认识一个最重要的规则
同学们先记住这一张表:
| 申请内存 | 释放内存 |
|---|---|
new int | delete p |
new int[10] | delete[] p |
allocator.allocate(10) | allocator.deallocate(p, 10) |
也就是:
new → delete new[] → delete[] allocate → deallocate谁借的,就尽量用对应的方法还。
这是今天最重要的规律。
二、先理解new
假设我们写:
int *p = new int;可以把它理解成:
“计算机,请给我一间房子,里面住一个
int。”
计算机就找了一块内存:
堆区 ┌─────────────┐ │ int │ │ 10 │ └─────────────┘ ↑ p例如:
*p = 10; cout << *p;输出:
10但是这块内存是我们自己申请的。
所以用完以后:
delete p;就是告诉计算机:
“这间房子我不用了,可以收回去了。”
三、delete p到底做了什么?
看这个例子:
int *p = new int; *p = 100; delete p;可以理解成:
new int ↓ 申请一块内存 ↓ ┌─────────┐ │ 100 │ └─────────┘ ↑ p delete p ↓ 把这块内存还给系统注意:
delete p删除的不是变量p本身。
这一点非常重要!
int *p = new int;这里其实有两个东西:
p │ │ 保存一个地址 ↓ ┌──────────┐ │ int对象 │ └──────────┘执行:
delete p;删除的是:
┌──────────┐ │ int对象 │ └──────────┘而不是指针变量:
p所以p这个指针变量本身还存在,只不过它里面原来的地址已经不能再拿来访问那个对象了。
四、那么为什么有delete[] p?
因为我们还可以一次申请很多个对象。
比如:
int *p = new int[5];意思是:
“请给我连续的5个
int房间。”
内存可以想象成:
p ↓ ┌────┬────┬────┬────┬────┐ │ 10 │ 20 │ 30 │ 40 │ 50 │ └────┴────┴────┴────┴────┘ 0 1 2 3 4于是:
p[0] = 10; p[1] = 20; p[2] = 30; p[3] = 40; p[4] = 50;因为我们申请的是:
new int[5]所以最后必须:
delete[] p;而不是:
delete p;五、delete和delete[]的区别
这可以记成一句特别简单的话:
一个对象用
delete,一群对象用delete[]。
例如:
一个int
int *p = new int; delete p;对应:
new ↓ 一个对象 ↓ delete一组int
int *p = new int[10]; delete[] p;对应:
new[] ↓ 多个对象 ↓ delete[]六、为什么不能搞错?
例如:
int *p = new int[10]; delete p; // ❌这是错误的。
因为:
new int[10]和
delete p不是一对。
正确的是:
int *p = new int[10]; delete[] p; // ✅反过来也一样:
int *p = new int; delete[] p; // ❌应该:
delete p; // ✅七、这里还有一个非常重要的原因:析构函数
如果是简单的:
int double char同学们可能觉得:
“反正都是内存,还不都是还回去吗?”
但是 C++ 里面还有一个非常重要的东西:
对象的析构函数。
例如:
class Student { public: ~Student() { cout << "学生对象消失了"; } };如果:
Student *p = new Student[3]; delete[] p;那么 C++ 会正确处理这三个对象的销毁。
可以理解成:
Student 1 → 析构 Student 2 → 析构 Student 3 → 析构 ↓ 释放整块内存所以:
delete[]不只是“释放一大片内存”,还要正确处理这一组对象的生命周期。
这也是为什么new[]必须对应delete[]。
八、现在来看一个新朋友:allocator
前面我们学习:
new它其实做了两件事情:
① 申请内存
② 创建对象
而 C++ 的:
allocator可以让我们把这两件事情拆开。
这就是它最重要的意义。
九、把new想成“一条龙服务”
例如:
Student *p = new Student;可以理解为你去了一家酒店:
“我要一个已经准备好的房间。”
酒店直接帮你:
找房子 ↓ 分配内存 ↓ 创建 Student ↓ 交给你所以:
new Student相当于:
申请内存 + 构造对象
十、allocator可以把事情拆开
例如:
allocator<int> alloc;可以理解成:
“我有一个专门负责管理
int内存的管理员。”
然后:
int *p = alloc.allocate(5);它的意思是:
给我一块能够放下 5 个
int的原始内存。
注意!
这里有一个非常重要的地方:
alloc.allocate(5);只是申请内存。
它并没有自动把 5 个int都创建出来。
十一、allocate和new[]最大的区别
看:
int *p = new int[5];这里:
new[] ↓ 申请内存 + 创建5个 int 对象而:
allocator<int> alloc; int *p = alloc.allocate(5);只有:
allocate ↓ 申请能够容纳5个 int 的内存还没有真正构造对象。
可以把它想象成:
allocate(5) ┌────┬────┬────┬────┬────┐ │ 空 │ 空 │ 空 │ 空 │ 空 │ └────┴────┴────┴────┴────┘ 只是准备了5个“座位”十二、那对象怎么创建?
这就需要进一步使用构造操作。
现代 C++ 可以使用:
std::allocator<int> alloc; int *p = alloc.allocate(5); alloc.construct(p, 10); alloc.construct(p + 1, 20); alloc.construct(p + 2, 30);现在才真正创建了对象。
可以理解成:
allocate ↓ 准备5个座位 ↓ construct ↓ 让对象坐进去例如:
allocate(5) ┌────┬────┬────┬────┬────┐ │ 空 │ 空 │ 空 │ 空 │ 空 │ └────┴────┴────┴────┴────┘ construct(p, 10) ┌────┬────┬────┬────┬────┐ │ 10 │ 空 │ 空 │ 空 │ 空 │ └────┴────┴────┴────┴────┘十三、用完怎么办?
如果使用了:
allocate()就应该使用:
deallocate()例如:
allocator<int> alloc; int *p = alloc.allocate(5); // 使用内存…… alloc.deallocate(p, 5);所以:
allocate ↓ deallocate是一对。
十四、把三组关系放在一起
这是今天最值得记住的一张表:
| 申请 | 作用 | 释放 |
|---|---|---|
new T | 申请内存 + 创建一个对象 | delete p |
new T[n] | 申请内存 + 创建 n 个对象 | delete[] p |
allocator.allocate(n) | 只申请原始内存 | allocator.deallocate(p,n) |
可以记成:
申请 释放 new T ───────────────→ delete p new T[n] ──────────────→ delete[] p allocator.allocate(n) ───────→ allocator.deallocate(p,n)十五、一个特别形象的比喻
假设学校有一个仓库。
new
你对管理员说:
“给我一个已经装修好的房间。”
管理员:
找房间 ↓ 装修 ↓ 交给你这就是:
new Studentnew[]
你说:
“给我10个已经装修好的房间。”
管理员:
找10个房间 ↓ 全部装修 ↓ 交给你对应:
new Student[10]最后:
delete[] p;管理员把这10个房间都处理掉。
allocator
这时候你说:
“先给我一块空地,我自己决定什么时候盖房子。”
管理员:
alloc.allocate(10);只给你:
一块可以放10个对象的空间然后你自己:
alloc.construct(...)决定什么时候建对象。
最后:
alloc.destroy(...)销毁对象,再:
alloc.deallocate(...)归还内存。
所以:
allocator 给了程序员更精细的“内存控制权”。
十六、为什么 C++ 要搞这么复杂?
同学们可能会问:
“直接
new不就行了吗?为什么还要allocator?”
这是一个非常好的问题。
因为很多时候,我们并不希望:
申请内存 ↓ 马上创建对象而是希望:
先准备一大片内存 ↓ 真正需要的时候再创建对象 ↓ 不用的时候销毁对象 ↓ 最后再释放内存这对于 C++ 的一些底层数据结构非常重要。
例如:
vector内部就需要非常精细地管理:
容量 capacity 元素 size 内存申请 对象构造 对象析构 内存释放所以理解allocator,实际上是在帮助小朋友理解:
vector、STL 容器到底是怎么管理内存的。
十七、一个很重要的“坑”:不要混着用
下面这些组合是正确的:
int *p = new int; delete p;int *p = new int[10]; delete[] p;allocator<int> a; int *p = a.allocate(10); a.deallocate(p, 10);但下面这些不要做:
int *p = new int[10]; delete p; // ❌以及:
int *p = new int; delete[] p; // ❌更不要:
allocator<int> a; int *p = a.allocate(10); delete[] p; // ❌因为:
申请内存和释放内存的方法必须匹配。
十八、再送同学们一个“内存管理口诀”
可以把它背下来:
new 对 delete,new[] 对 delete[];
allocate 对 deallocate;
一个对象 delete,一组对象 delete[];
allocator 更底层,内存和对象可以分开管理。
十九、最后做一道小测试
看到下面的代码:
int *p1 = new int; int *p2 = new int[20]; allocator<int> a; int *p3 = a.allocate(30);问:
①p1怎么释放?
delete p1;②p2怎么释放?
delete[] p2;③p3怎么释放?
a.deallocate(p3, 30);所以最后变成:
delete p1; delete[] p2; a.deallocate(p3, 30);二十、给小学生真正要掌握的三个层次
这个知识点不要一上来就要求大家背很多规则,可以分成三个层次。
第一层:必须掌握 ⭐⭐⭐⭐⭐
new → delete new[] → delete[]知道:
一个用
delete,一组用delete[]。
第二层:理解 ⭐⭐⭐⭐
知道:
new不仅仅是申请内存,还会创建对象。
而:
delete不仅仅是释放内存,还会处理对象的销毁。
第三层:拓展 ⭐⭐⭐
理解:
allocator把:
“内存申请”
和
“对象创建/销毁”
进行了更加精细的分离。
这其实已经开始进入STL 容器底层实现的世界了。
最后用一张图彻底记住
C++ 动态内存 │ ┌───────────┼───────────┐ │ │ │ new T new T[n] allocator │ │ │ ↓ ↓ ↓ 一个对象 一组对象 原始内存 │ │ │ ↓ ↓ ↓ delete p delete[] p deallocate而最核心的一句话是:
🧠
delete和delete[]的区别,不是“删一个变量还是删很多变量”,而是它们必须分别匹配new和new[];allocator则进一步把“申请内存”和“创建对象”这两件事情拆开了。