当前位置: 首页 > article >正文

五、C++内存管理机制 —— primitives(侯捷)

侯捷 C++八部曲笔记汇总 - - - 持续更新 ! ! !
一、C++ 面向对象高级开发
1、C++面向对象高级编程(上)
2、C++面向对象高级编程(下)
二、STL 标准库和泛型编程
1、分配器、序列式容器
2、关联式容器
3、迭代器、 算法、仿函数
4、适配器、补充
三、C++ 设计模式
四、C++ 新标准
五、C++ 内存管理机制
1、primitives
2、std::allocator
3、malloc/free
4、other allocators
5、loki::allocator
六、C++ 程序的生前和死后

五、C++内存管理机制 —— 基础工具 primitives(侯捷)

  • new、delete 表达式
    • 基本用法
    • array new、array delete
    • placement new
  • operator new()、operator delete()函数
    • 重载 ::operator new / ::operator delete
    • 重载 operator new() / operator delete()

👉 该课程能让你:从平地到万丈高楼!—— 侯捷
👉 做到:心中自有丘壑!
💪 源码之前,了无秘密!

所有内存管理的最终动作都要跑到malloc去,所以malloc的效率至关重要。

  • 即,最高阶是使用STL中的内存分配器,而STL的内存分配器实现是通过newnew[]new()等较为低阶的函数,但终归会到mallocfree这两种C函数上来!
  • 一般不会到最下面操作系统层次,不然程序不具有可移植性!

C++内存使用途径:
在这里插入图片描述

C++内存分配的基本工具如下:
在这里插入图片描述
🏃 四个层面使用栗子:

void* p1 = malloc(512); //512 bytes
free(p1);

complex<int>* p2 = new complex<int>; //one object
delete p2;

void* p3 = ::operator new(512);  //512 bytes (低层就是调用malloc)
::operator delete(p3); //(低层就是调用free)

//以下使用C++ 标准库提供的allocators。
//GNUC2.9  (以下函数为static,定要通过object调用)
void* p4 = alloc::allocate(512);//分配512bytes
alloc::deallocate(p4, 512);

//GNUC4.9  (以下函数为non-static,定要通过object调用)
void* p5 = allocator<int>().allocate(5); //分配5个int
allocator<int>().deallocate((int*)p5, 5);//归还个数也要告诉它

new、delete 表达式

基本用法

⭐️ new的过程分为三步:

  1. 分配内存(指定类大小的空间):new->operator new->malloc
  2. 将指针转化为对应的类类型
  3. 通过指针调用构造函数

在这里插入图片描述
在上图右上角operator new的源码中:

  • malloc没有成功时(一般很少出现),会调用你设定的函数new handler, 在这里面释放可以释放的内存,然后再调用malloc看是否成功;
  • std::nothrow_t& _THROW0()参数,表示这个函数不抛异常,取而代之的是返回一个空指针,用户通过判断是否为空指针来判断是否分配成功。

👉 new handler

  • operator new没有能力为你分配出你所申请的memory,会抛出一个std::bad_alloc exception的异常。某些老编译器则是返回 0,你仍可以令编译器那么做:
new (nothrow) Foo;
  • 抛出exception(异常)之前会先(不止一次)调用一个可由 client (用户)指定的handler,以下是new handler 的形式和设定方法:
typedef void(*new_handler)();
new_handler set_new_handler(new_handler p) throw();
//set_new_handler 为C++提供的一个函数

这个new handler一般做两件事情

  1. 让更多的内存可用;
  2. 调用abort()或者exit()

举个栗子:

在这里插入图片描述

👉 =default,=delete

=default:表示使用默认版本拷贝构造函数拷贝赋值函数析构函数,还有operator new/new[]operator delete/delete[],如果没有写的话,编译器会给你一套编译器合成版);
=default:表示这个函数不要。

⭐️ delete的过程:
4. 先调用析构函数,对象本身的内存就会被释放掉,对象也就死亡了;
5. 然后释放内存delete -> operator delete -> free

在这里插入图片描述

构造函数不能被直接调用,析构函数可以被直接调用!

在这里插入图片描述

array new、array delete

⭐️ array new是分配一个对象数组:

  • 通常容易犯得一个错误是在delete的时候忘记在delete后面加 []导致内存泄漏的问题

在这里插入图片描述

正如上图所说的:

  • 对于类中没有指针的类,不加 [] 可能问题不大,因为没有指针的类析构函数本来也就没有什么大的作用;
  • 但是,如果有指针,忘记写 [] ,那么 delete 只会触发一次析构函数delete掉一个指针指向的内存,其他指针指向的内存就会泄露。
    • 如上图的 psa 析构,str2str3指向的地址会发生内存泄漏(析构的顺序依编译器而定)。

使用例子:

在这里插入图片描述

👉 没有办法直接调用构造函数设初值,只能绕个弯使用placement new来设初值,形式如下:

//定点的new
new(tem)A(i);			//tem是一个指针

下面两张图分别表示有指针没指针的对象数组分配内存的区别:

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

对于分配一个对象数组,他会把数组的大小也放到存放对象数组的内存块的开头:

  • 如果在delete内存的时候不加 [],编译器会把他当成一个一般的指针,指向一块对象;
  • 它按照一块对象的方式来解析布局,但是存储的是3块,整个布局就乱掉了,就会报错!

placement new

array new调用的是类的默认构造函数,还需要对数组中的对象进行真正的构造,这就需要placement new允许我们将对象分配在已经构建的内存中

placement new 就等同于调用构造函数。他不会进行内存分配,具体过程如下:

  1. 调用重载的operator new,和上面的形式不一样,还传入了第二个参数buf定点),用于返回已经分配好的内存;
  2. 转型;
  3. 调用构造函数
    在这里插入图片描述

operator new()、operator delete()函数

  • 一般在类里可重载;
  • 也可以重载全局::operator new / ::operator delete,但很少,不然可能影响很大!

在这里插入图片描述
容器自己也有一套重载:

在这里插入图片描述

重载 ::operator new / ::operator delete

⭐️ 重载全局的:

在这里插入图片描述

⭐️ 在类中重载::operator new / ::operator delete更有用(array new / array delete重载也是一样的方法):

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

在类里重载operator newoperator delete前面一般要加上static(静态的):

  • 因为在创建对象的时候,对象还没有创建成功,因此是无法通过对象来调用一般的函数;
  • 所以必须是静态的,这样才能不通过对象就能调用起来。

在这里插入图片描述

重载 operator new() / operator delete()

⭐️ 我们可以重载class member operator new()的前提是

  • 每一个版本的声明都必须有独特的参数列,其中第一个参数必须是size_t,其余参数以 new 所制定的 placement arguments为初值。

出现于new (…)小括号内的便是所谓的 placement arguments。形式例如:

//placement new
Foo* pf = new(300, 'c') Foo;

在这里插入图片描述
⭐️ 我们可以重载class member operator delete()

  • 但它们绝不会被delete调用;
  • 只有当new 所调用的 ctor 抛出 exception,才会调用这些重载版的 operator delete()
  • 它只可能这样被调用,主要用来归还未能完全创建成功的object所占用的memory

在这里插入图片描述

注:仅供学习参考,如有不足,欢迎指正!👍👍👍


http://www.kler.cn/a/17206.html

相关文章:

  • MySQL重难点(一)索引
  • 【JVM】关于JVM的内部原理你到底了解多少(八股文面经知识点)
  • 【C++】一种针对代码的连续条件检查方案,累计布尔结果
  • [代码随想录Day10打卡] 理论基础 232.用栈实现队列 225. 用队列实现栈 20. 有效的括号 1047. 删除字符串中的所有相邻重复项
  • ❤React-React 组件通讯
  • FFmpeg 4.3 音视频-多路H265监控录放C++开发十三:将AVFrame转换成AVPacket。视频编码原理.编码相关api
  • C#学习系列之throw new ApplicationException
  • 图书管理系统【控制台+MySQL】(Java课设)
  • 武忠祥老师每日一题||定积分基础训练(六)
  • 体验 ChatGLM-6B
  • 晚唐诗人杜荀鹤及其十首古诗赏析
  • Soft-RoCE部署及通信测试
  • 全方位揭秘!大数据从0到1的完美落地之运行流程和分片机制
  • MySQL 表操作
  • asp.net基于web的校园美食派送配送系统
  • 数据恢复软件EasyRecovery16下载安装步骤教程
  • SpringBoot 中的加密模块
  • 辞了外包,上岸字节我落泪了,400多个日夜没人知道我付出了多少....
  • 【C语言】typedef关键字
  • RocketMQ 底层实现原理
  • 神秘的IP地址8.8.8.8地址到底是什么?为什么会被用作DNS服务器地址呢?
  • GPT详细安装教程-GPT软件国内也能使用
  • JVM学习(九):堆
  • 海思芯片(hi3536av100)启动模式选择
  • Linux centos搭建web服务器
  • 利用Google Colab免费使用GPU服务器详细攻略