ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

小学生学C++编程语法知识(C++申请内存与释放内存)

小学生学C++编程语法知识(C++申请内存与释放内存) new、delete、new[]、delete[]、allocator都在解决一个问题“我向计算机借了一块内存用完以后应该怎样把它还回去”一、先认识一个最重要的规则同学们先记住这一张表申请内存释放内存new intdelete pnew int[10]delete[] pallocator.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[]。例如一个intint *p new int; delete p;对应new ↓ 一个对象 ↓ delete一组intint *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可以把事情拆开例如allocatorint alloc;可以理解成“我有一个专门负责管理int内存的管理员。”然后int *p alloc.allocate(5);它的意思是给我一块能够放下 5 个int的原始内存。注意这里有一个非常重要的地方alloc.allocate(5);只是申请内存。它并没有自动把 5 个int都创建出来。十一、allocate和new[]最大的区别看int *p new int[5];这里new[] ↓ 申请内存 创建5个 int 对象而allocatorint alloc; int *p alloc.allocate(5);只有allocate ↓ 申请能够容纳5个 int 的内存还没有真正构造对象。可以把它想象成allocate(5) ┌────┬────┬────┬────┬────┐ │ 空 │ 空 │ 空 │ 空 │ 空 │ └────┴────┴────┴────┴────┘ 只是准备了5个“座位”十二、那对象怎么创建这就需要进一步使用构造操作。现代 C 可以使用std::allocatorint 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()例如allocatorint alloc; int *p alloc.allocate(5); // 使用内存…… alloc.deallocate(p, 5);所以allocate ↓ deallocate是一对。十四、把三组关系放在一起这是今天最值得记住的一张表申请作用释放new T申请内存 创建一个对象delete pnew T[n]申请内存 创建 n 个对象delete[] pallocator.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;allocatorint a; int *p a.allocate(10); a.deallocate(p, 10);但下面这些不要做int *p new int[10]; delete p; // ❌以及int *p new int; delete[] p; // ❌更不要allocatorint a; int *p a.allocate(10); delete[] p; // ❌因为申请内存和释放内存的方法必须匹配。十八、再送同学们一个“内存管理口诀”可以把它背下来new 对 deletenew[] 对 delete[]allocate 对 deallocate一个对象 delete一组对象 delete[]allocator 更底层内存和对象可以分开管理。十九、最后做一道小测试看到下面的代码int *p1 new int; int *p2 new int[20]; allocatorint 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则进一步把“申请内存”和“创建对象”这两件事情拆开了。
返回列表