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

news/2024/11/24 11:42:46/

侯捷 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.ppmy.cn/news/57166.html

相关文章

【初学人工智能原理】【2】方差代价函数:知错

前言 本文教程均来自b站【小白也能听懂的人工智能原理】&#xff0c;感兴趣的可自行到b站观看。 本文【原文】章节来自课程的对白&#xff0c;由于缺少图片可能无法理解&#xff0c;故放到了最后&#xff0c;建议直接看代码&#xff08;代码放到了前面&#xff09;。 代码实…

unity GI Shader 实现

之前分享了一篇对unity全局光照的解析&#xff0c;里面提到了一些东西&#xff0c;需要在Shader内实现&#xff0c;在这一篇补上。 要实现对全局GI的shader实现&#xff0c;我们可以通过对unity内置的Lit进行解析查看。 烘焙的方式有很多种&#xff0c;选择合适的方式烘焙和使…

Android Jetpack 从使用到源码深耕【LiveData 从实践到原理 】(二)

上文,我们就一个实例需求,引入了LiveData进行了实现,大家通过前后的编码实现方案对比,可以感受到LiveData的好用。不由的为jetpack组件的开发者点赞。 Android Jetpack 从使用到源码深耕【LiveData 从实践到原理 】(一) Android Jetpack 从使用到源码深耕【LiveData 从实…

【热门框架】Maven中聚合,继承指的是什么?有什么作用?

Maven中的聚合和继承是两个重要的功能&#xff0c;用于管理多个项目的共同部分。 1. 聚合 Maven中的聚合&#xff08;Aggregation&#xff09;指的是将多个子项目聚合成一个父项目的过程。聚合的语法如下&#xff1a; xml <project> <modelVersion>4.0.0<…

2022年职业教育技能大赛网络安全 linux系统渗透提权

B-10&#xff1a;Linux 系统渗透提权 任务环境说明&#xff1a; ✓ 服务器场景&#xff1a;Server2202&#xff08;关闭链接&#xff09; ✓ 用户名&#xff1a;hacker 密码&#xff1a;123456 使用渗透机对服务器信息收集&#xff0c;并将服务器中 SSH 服务端口号作为 flag…

图片人群计数模型代码运行指南

PaperWithCode 八大数据集模型排名&#xff1a;https://paperswithcode.com/task/crowd-counting 搜索关键词 “人群计数”&#xff08;crowd counting,crowd指的是人而不是拥挤的人&#xff1b;Counting People&#xff09;;“人流计数”;“人流量统计”&#xff08;&#x…

语义分割学习笔记(二)转置卷积

目录 1.转置卷积Transposed Convolution概念 2.转置卷积操作步骤 3.转置卷积参数 4.实战案例 推荐课程&#xff1a;转置卷积&#xff08;transposed convolution&#xff09;_哔哩哔哩_bilibili 感谢霹雳吧啦Wz&#xff0c;真乃神人也。 1.转置卷积Transposed Convolutio…