new和delete快被问麻了,必须整理一波儿底层原理!

被翻来覆去的new和delete折磨麻了!!!

今天含泪整理一波new和delete的底层原理,希望我们再也不会被这个问题给拦住了,一把拿下它!

我们在手捧那两本经典的《C++ primer plus》和《C++ primer》书籍的时候,书上清楚地写着我们在堆中申请和释放内存的时候需要使用new和delete,new [] 和 delete [] 并且必须要配对使用。但是我们只是知道要记住,但是大多都是只知其然,而不知其所以然,那么今天就来深入的讨论讨论,为什么要配对使用,我要是不配对是使用的话会怎么样。

new和delete到底做了什么

我们先来看一个经典的图:

new和delete为什么要成对儿出现

从图中我们可以清楚的看到:

一个C++应用程序在堆中分配内存的过程一共有三种方式:

  1. 应用程序—>C++标准库—>new、delete相关—>malloc和free—>HeapAlloc、VirtualAlloc(操作系统底层API)
  2. 应用程序—>new、delete相关—>malloc和free—>HeapAlloc、VirtualAlloc(操作系统底层API)
  3. 应用程序—>malloc和free—>HeapAlloc、VirtualAlloc(操作系统底层API)
  4. 应用程序—>HeapAlloc、VirtualAlloc(操作系统底层API)

我们可以看到以下几点:

  • 操作系统的API是我们开发应用程序绕不过去的,但是直接调用操作 系统API也不是我们大多数情况需要的,因为我们更多的时候需要写出多平台的代码,而不是针对某一个操作系统来写
  • 尽管我们调用new和delete,但是其本质还是调用的C语言库中使用的malloc和free两个函数(这是我们一会儿要着重关注和研究的)

一个对象的“出生入死”

Object* obj = new Object; //出生
··· ···
delete obj    //消灭

这个是我们new 一个对象然后调用delete来删除这个对象,那么其本质是什么嘞

我们new的操作在编译器眼中大致是什么样子:

Object* obj;
try{
    void* mem = operator new( sizeof(Object) ); //分配内存
    obj = static_cast(mem);        //指针转换
    obj->Objcect::Object();                //原地构造
}
catch( std::bad_alloc ){
    //失败就不执行构造
}

operator new 本质上是分配内存,调用的是malloc函数,具体我就不详细写出来啦,感兴趣的可以在评论区一起进行交流。

我们delete的操作在编译器眼中大致是什么样子:

pc->~Object();        //先析构
operator delete(obj);    //释放内存

operator delete本质上是释放内存,调用的是free函数,具体我也就不详细写出来啦。

再来看看new []和delete []

Object* obj = new Object[3];    //三次构造
··· ···
delete[] obj;    //三次析构

可以从上面看出来,根据知识迁移可知,当我们申请的是一个对象数组的时候,也同样会经历这个过程,有几个对象就会调用几次构造函数,有几个对象就会调用几次析构函数。

数组对象内存图

我们申请的内存数组会在数组的头部存储一些相关的信息,具体的大小和编译器的实现有关。但是都会记录这个数组的个数。因此,我们构造次数==析构次数

思考归纳

那么传说中的,如果我们不进行delete [] 来释放内存的话,为什么会出现内存泄漏呢?

其实本质上来说不是这样的,就算我们使用 delete 和 new [] 搭配起来,也不会在这里发生内存泄漏,因为数据头中标记了个数,所以一定会把这块儿内存free 掉的。

那内存泄漏是指的什么?

其实这样会导致这三个对象的析构函数只调用一次,也就是最上面的那个析构函数,其他的两个的析构函数不会被调用,所以现在明白了吧,内存泄漏指的是在这个对象中申请的内存,由于没有调用析构函数,导致那块儿内存不会被释放所以泄漏了内存。

如果我们new的不是对象而是内置类型,或者说对象中并不含有 指向其他堆内存的指针,那么理论上来说就算不使用delete也不会造成内存泄漏,但是我们平时还是要遵守,这是一个好的习惯,搭配起来的话就不会出现各种各样奇怪的问题了。

如果觉得有用的话,大家记得点赞和收藏哦~


往期回顾:

大厂必问题:三次握手、四次挥手、最大报文段长度、流量控制实践
微软终面题之汉诺塔:动图演示太好理解啦!
把我珍藏的学习笔记拿出来分享给大家,一起学习一起进步
快手、美团等都在问的线程池它来了
小白也能进大厂:希望我走过的路,你可以拥有捷径


#面试那些事##春招##实习##秋招##C/C++##校招##社招##提前批#
全部评论
C++是真难呀
2 回复 分享
发布于 2022-04-22 21:23
然而面试官还继续问了malloc的底层实现
2 回复 分享
发布于 2022-04-24 14:54
这也不算底层原理啊。。下到malloc和操作系统实现那才是
2 回复 分享
发布于 2022-05-06 14:17
这也太底层了,厉害
1 回复 分享
发布于 2022-04-22 21:51
比Java复杂多了。。
点赞 回复 分享
发布于 2022-04-22 21:37
析构函数,学java后都忘记了有这个函数了
点赞 回复 分享
发布于 2022-04-22 22:06
热榜通知书!!
点赞 回复 分享
发布于 2022-04-24 14:13
感谢分享
点赞 回复 分享
发布于 2022-04-26 20:48
有问malloc实现吗?
点赞 回复 分享
发布于 2022-06-03 14:07

相关推荐

09-25 10:34
东北大学 Java
多面手的小八想要自然醒:所以读这么多年到头来成为时代车轮底下的一粒尘
点赞 评论 收藏
分享
71 424 评论
分享
牛客网
牛客企业服务