云计算百科
云计算领域专业知识百科平台

C++内存管理:编译器的优化、C++内存划分、new、delete关键字及其底层

在这里插入图片描述

博主名称:deM0one

⭐️C++专栏:
C++

文章目录

    • @[toc]
  • C++ 内存管理详解(new/delete 与内存分区)
    • 一、引言
    • 二、类与对象收尾:对象拷贝时的编译器优化
      • 2.1 编译器为什么会优化拷贝
      • 2.2 常见的优化场景
      • 2.3 演示与结论
    • 三、C++ 程序的内存分区
      • 3.1 四大区域
      • 3.2 指针与指向的内容可能在不同区域
      • 3.3 经典练习:这些变量都在哪个区?
      • 3.4 核心理解:分区实际上分的是"生命周期"
    • 四、回顾 C 语言的动态内存管理
    • 五、C++ 的 new / delete 关键字
      • 5.1 基本用法
      • 5.2 申请空间的同时初始化
      • 5.3 自定义类型的行为
      • 5.4 new 失败的处理方式
    • 六、内存与虚拟内存
    • 七、operator new 与 operator delete(重点)
      • 7.1 定位
      • 7.2 调用关系
    • 八、new / delete 的实现原理(重点)
      • 8.1 内置类型
      • 8.2 自定义类型
    • 九、new / delete 与 new[] / delete[] 的匹配(易错点)
      • 9.1 匹配规则
      • 9.2 反例演示
      • 9.3 结论
    • 十、总结
      • 10.1 全文回顾
      • 10.2 常见面试题自测清单
    • 任何的建议与批评欢迎指出,我们共同进步!

C++ 内存管理详解(new/delete 与内存分区)

今日学习: 类与对象收尾,编译器的自动优化何时优化,在哪里优化

一、引言

  • 内存是程序运行的核心资源之一(CPU、GPU一种特殊的CPU、内存)
  • 为什么要理解内存管理:写出高效、不出错的 C++ 程序
  • 本篇博客的阅读路线图(先分区、再用法、后原理)

二、类与对象收尾:对象拷贝时的编译器优化

2.1 编译器为什么会优化拷贝

  • 现代编译器会在不影响正确性的前提下,尽可能减少传参和返回过程中可省略的拷贝
  • C++ 标准没有严格规定优化方式,由各编译器自行处理

2.2 常见的优化场景

  • 同一表达式内连续拷贝的"合并优化"
  • 更激进编译器的跨行、跨表达式合并优化 在这里插入图片描述 编译器能够将一次构造,一次拷贝构造直接优化成一次构造,减少多余的构造,提高效率。 在这里插入图片描述 此处编译器将一次构造,一次传值返回的拷贝构造的临时对象,优化成直接用1来构造临时对象返回,将aa的构造给优化掉了。 在这里插入图片描述 此处编译器却没有优化这是为什么?因为ret=f2()属于赋值拷贝,没有传值传参的过程,也不是对象的构造,所以编译器没有做激进的优化。

2.3 演示与结论

在这里插入图片描述

  • 示例:f2().Print() 直接对临时对象调用成员函数,观察拷贝构造的调用次数(优化后为 0 次)
  • 结论:理解拷贝构造与赋值重载的真实调用时机

三、C++ 程序的内存分区

3.1 四大区域

  • 栈区:局部变量、函数参数,编译器自动分配释放
  • 堆区:动态申请的内存,需手动管理
  • 数据段(静态区):全局变量、静态变量
  • 代码段(常量区):常量字符串、程序指令

3.2 指针与指向的内容可能在不同区域

  • 指针变量本身在栈上,指向的内容可能在栈、堆、常量区等

3.3 经典练习:这些变量都在哪个区?

在这里插入图片描述

3.4 核心理解:分区实际上分的是"生命周期"

四、回顾 C 语言的动态内存管理

  • 4.1 malloc / calloc / realloc / free 的基本用法
  • 4.2 三者的区别(初始化与否、扩容方式)
  • 4.3 为后文与 new/delete 对比做铺垫

五、C++ 的 new / delete 关键字

5.1 基本用法

  • new int / new int[10],delete p / delete[] p

5.2 申请空间的同时初始化

  • new int(5)
  • new int[10]{1, 2, 3, 4, 5}

5.3 自定义类型的行为

  • new 会调用构造函数;没有默认构造函数时需要显式传值
  • delete 会调用析构函数

5.4 new 失败的处理方式

  • new 失败会抛异常(区别于 malloc 返回 NULL)
  • try/catch 捕获示例:申请超大空间,输出异常信息

六、内存与虚拟内存

  • 6.1 32 位系统:约 4G 虚拟地址空间
  • 6.2 64 位系统:2^64 个字节(约 160 多亿 G)
  • 6.3 虚拟内存的直观理解(地址空间大小由系统位数决定)

七、operator new 与 operator delete(重点)

7.1 定位

  • new/delete 是用户使用的操作符
  • operator new/operator delete 是系统提供的全局函数

7.2 调用关系

  • new 底层调用 operator new 申请空间
  • delete 底层调用 operator delete 释放空间

八、new / delete 的实现原理(重点)

8.1 内置类型

  • 与 malloc/free 基本类似
  • 区别一:new/delete 管理单个元素,new[]/delete[] 管理连续空间
  • 区别二:new 失败抛异常,malloc 失败返回 NULL

8.2 自定义类型

  • new 的原理:
  • 调用 operator new 申请空间
  • 在申请的空间上执行构造函数
  • delete 的原理:
  • 在空间上执行析构函数
  • 调用 operator delete 释放空间
  • 关键点:new 的内核是 malloc,delete 的内核是 free
  • 补充:对于内置类型,用 free 还是 delete 没有区别

九、new / delete 与 new[] / delete[] 的匹配(易错点)

9.1 匹配规则

  • delete 是针对 new 的,delete[] 是针对 new[] 的

9.2 反例演示

  • new A[10] 却用 delete p 会怎样

在这里插入图片描述 编译器对于多个对象的数组的时候会在头上多开四个字节,用于存储数组元素个数,因为B类型没有写析构函数所以编译器将多余的四字节给优化掉了,但是A写了析构函数,却错误地配置了delete,没有正确调用析构函数,释放位置错误导致报错

9.3 结论

  • 二者一定要匹配使用,不要错配

十、总结

10.1 全文回顾

C++程序的内存划分与C语言基本一脉相承,这也体现了C++对C语言的兼容,C++中新增的new和delete关键字相比C语言的malloc,可以更便捷地实现内存的申请和初始化,是对代码简洁性的提升,另外对于malloc和free,new和delete,以及new[]和delete[]最好搭配使用,不可错配,减少报错和崩溃的风险。

10.2 常见面试题自测清单

  • 各变量分别存在哪个区?
  • new 和 malloc 的区别?
  • new 一个对象经历了哪些步骤?
  • delete[] 和 delete 混用会有什么问题?

任何的建议与批评欢迎指出,我们共同进步!

赞(0)
未经允许不得转载:网硕互联帮助中心 » C++内存管理:编译器的优化、C++内存划分、new、delete关键字及其底层
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!