当前位置:   article > 正文

【C++】拿下! C++中的内存管理

【C++】拿下! C++中的内存管理

在这里插入图片描述

1 C++ 的内存分布

内存管理是十分重要的内容,企业开发中多有服务器宕机的大事故,比如:

  1. B站崩了两次:
    2023年3月5日晚20:20左右,许多网友表示在使用B站时,手机和电脑端都无法访问视频详情页,且手机端无法查看收藏夹与历史记录。
    8月4日晚间,距离上次事故5个月后,又有许多网友反馈B站图片(视频封面)无法加载、视频无法打开、视频一直在缓冲。
  2. 腾讯一级事故:
    2023年3月29日凌晨,腾讯旗下的微信和QQ等业务曾出现崩溃状况,包括微信语音对话、朋友圈、微信支付,以及QQ文件传输、QQ空间和QQ邮箱在内的多个功能无法使用。

其中内存管理可能占有一定原因,只有我们打好内存管理的基础才能为大家做出贡献,那不然就只能赶快跑路了。
首先我们就要了解内存分布的情况是什么样的。
在这里插入图片描述

其中

  1. 栈又叫堆栈—非静态局部变量、函数参数、返回值等等。最重要的栈是向下增长的!空间有限但效率较高。
  2. 内存映射段是高效的 I/O映射方式,用于装载一个共享的动态内存库。用户可以使用系统接口创建共享内存,叫做进程间通信。
  3. 堆用于程序运行时的动态内存分配,注意堆是向上增长的。会产生内存碎片(如果不停开辟空间会导致内存浪费)且效率较慢。
  4. 数据段—储存全局数据和静态数据。
  5. 代码段—可执行的代码 / 只读常量。

堆和栈是我们常用到的区域,栈不需要我们进行管理,需要我们多加注意的就是堆区域

2 C语言的内存管理

我们回忆一下C语言的内存管理,大概是下面四个函数的使用:

  1. malloc 直接开辟空间
  2. calloc 开辟并初始化空间
  3. realloc 扩容
  4. free 释放

接下来我们来看C++ 的内存管理,来欣赏祖师爷的绝妙手笔~

3 C++的内存管理

首先C语言的内存管理可以在C++中使用,但是有些地方就显得比较复杂,因此我们需要C++的内存管理
C++的内存管理是通过new 操作符 和 delete 操作符来实现的。

3.1 内置类型操作

首先来看对于内置类型的操作:

int main(){
	//开辟申请一个int的空间
	int* p1 = new int;
	//开辟申请一个int的空间,并初始化为10
	int* p2 = new int(10);
	//开辟申请 3 个int的空间	
	int* p3 = new int[3];
	//开辟申请 3 个int的空间,并初始化为1,2,3
	int* p4 = new int[3] {1, 2, 3};
	//释放空间
	delete p1;
	delete p2;
	delete[] p3;
	delete[] p4;
	 
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

在这里插入图片描述
通过一个new 就可以完成复杂的开辟空间操作。
在这里插入图片描述

注意:
每次使用完空间,一定要释放掉。申请和释放单个元素的空间,使用new和delete操作符,申请和释放连续的空间,使用
new[]和delete[],注意:匹配起来使用

3.2 自定义类型操作

对于自定义类型 new/delete 和 malloc/free最大区别是 new / delete对于【自定义类型】除了开空间还会调用构造函数和析构函数
来看使用:

#include"Date.h"

int main() {
	Date* p1 = new Date(2024, 2, 28);
	p1->show();
	delete p1;

	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

在这里插入图片描述
注意:在申请自定义类型的空间时,new会调用构造函数,delete会调用析构函数,而malloc与free不会

4 operator new与operator delete函数(重点)

new 和 delete 是用户进行动态内存申请和释放的操作符,而 operator new与operator delete 是系统提供的全局函数,new 在底层就是通过调用operator new 来申请空间,delete就是通过operator delete 来释放空间。
到这里我们大概就知道了operator new与operator delete函数应该是对 malloc 和 free的封装。
我们进入反汇编就可以验证这一点:


// new() Fallback Ordering
//
// +----------+
// |new_scalar<---------------+
// +----^-----+               |
//      |                     |
// +----+-------------+  +----+----+
// |new_scalar_nothrow|  |new_array|
// +------------------+  +----^----+
//                            |
//               +------------+----+
//               |new_array_nothrow|
//               +-----------------+

_CRT_SECURITYCRITICAL_ATTRIBUTE
void* __CRTDECL operator new(size_t const size)
{
00007FF7C6A17D60  mov         qword ptr [rsp+8],rcx  
00007FF7C6A17D65  sub         rsp,38h  
    for (;;)
    {
        if (void* const block = malloc(size))
00007FF7C6A17D69  mov         rcx,qword ptr [size]  
00007FF7C6A17D6E  call        malloc (07FF7C6A11401h)  
00007FF7C6A17D73  mov         qword ptr [rsp+20h],rax  
00007FF7C6A17D78  cmp         qword ptr [rsp+20h],0  
00007FF7C6A17D7E  je          operator new+27h (07FF7C6A17D87h)  
        {
            return block;
00007FF7C6A17D80  mov         rax,qword ptr [rsp+20h]  
00007FF7C6A17D85  jmp         operator new+4Bh (07FF7C6A17DABh)  
        }

        if (_callnewh(size) == 0)
00007FF7C6A17D87  mov         rcx,qword ptr [size]  
00007FF7C6A17D8C  call        _callnewh (07FF7C6A1150Ah)  
00007FF7C6A17D91  test        eax,eax  
00007FF7C6A17D93  jne         operator new+49h (07FF7C6A17DA9h)  
        {
            if (size == SIZE_MAX)
00007FF7C6A17D95  cmp         qword ptr [size],0FFFFFFFFFFFFFFFFh  
00007FF7C6A17D9B  jne         operator new+44h (07FF7C6A17DA4h)  
            {
                __scrt_throw_std_bad_array_new_length();
00007FF7C6A17D9D  call        __scrt_throw_std_bad_array_new_length (07FF7C6A1170Dh)  
            }
00007FF7C6A17DA2  jmp         operator new+49h (07FF7C6A17DA9h)  
            else
            {
                __scrt_throw_std_bad_alloc();
00007FF7C6A17DA4  call        __scrt_throw_std_bad_alloc (07FF7C6A111E0h)  
            }
        }

        // The new handler was successful; try to allocate again...
    }
00007FF7C6A17DA9  jmp         operator new+9h (07FF7C6A17D69h)  
}
00007FF7C6A17DAB  add         rsp,38h  
00007FF7C6A17DAF  ret  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61

在这里插入图片描述
显然operator new与operator delete函数应该是对 malloc 和 free的封装。

5 new和delete的实现原理

5.1 内置类型

如果申请的是内置类型的空间,new和malloc,delete和free基本类似,不同的地方是:
new/delete申请和释放的是单个元素的空间,new[]和delete[]申请的是连续空间,而且new在申请空间失败时会抛异常,malloc会返回NULL,需要我们来判断异常

5.2 自定义类型

new的原理

  1. 调用 operator new 函数申请空间
  2. 在申请的空间上执行构造函数,完成对象的构造。

delete的原理

  1. 在空间上执行析构函数,完成资源清理工作。
  2. 调用operator delete函数释放空间。

new T[ N ] 的原理

  1. 调用operator new [ ]函数,在operator new[ ]实际调用operator new 函数完成N个对象空间的申请。
  2. 在申请的空间执行N次构造函数。
  3. 注意 开辟空间会多开一些来储存个数N。

lete[]的原理

  1. 在释放的对象空间上执行N次析构函数,完成N个对象中资源的清理
  2. 调用operator delete[]释放空间,实际在operator delete[]中调用operator delete来释放空间

在这里插入图片描述
在这里插入图片描述

6 总结

malloc/free和new/delete的区别

malloc/free和new/delete的共同点是:都是从堆上申请空间,并且需要用户手动释放。
不同的地方是:

  1. malloc和free是函数,new和delete是操作符
  2. malloc申请的空间不会初始化,new可以初始化
  3. malloc申请空间时,需要手动计算空间大小并传递,new只需在其后跟上空间的类型即可,如果是多个对象,[]中指定对象个数即可
  4. malloc的返回值为void*, 在使用时必须强转,new不需要,因为new后跟的是空间的类型
  5. malloc申请空间失败时,返回的是NULL,因此使用时必须判空,new不需要,但是new需要捕获异常
  6. 申请自定义类型对象时,malloc/free只会开辟空间,不会调用构造函数与析构函数,而new在申请空间后会调用构造函数完成对象的初始化,delete在释放空间前会调用析构函数完成空间中资源的清理

Thanks♪(・ω・)ノ谢谢阅读!!!

下一篇文章见!!!

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/277889
推荐阅读
相关标签
  

闽ICP备14008679号