文章目录

  • 2 内存分配的每一个层面
  • 3 4个层面的基本用法
  • 4.基本构件之一new delete expression(上)
  • 5.基本构件之一newdelete expression(中)
  • 7.Array new
  • 8.placement new
  • 9.重载
  • 10.重载示例(上)
  • 11.重载示例(下)

2 内存分配的每一个层面

对于C++的应用程序来讲,我们一般有三种调用函数的方式来分配内存;

  • malloc函数;
  • new的相关操作符和函数 ;
  • allocator的相关分配器;


而从图我们也知道,每一层都是对下一层的封装;也就是说,假如我直接调用标准库的std::allocator来说,也是在里面封装了new之类的m,而对于new来说,下次一层也是封装malloc;
其实我们讨论到malloc底层即可,不用再深入到O.S的API ;


C++的内存分配函数的分类:从4个层面去探讨它;


3 4个层面的基本用法

  • 我们知道对于malloc来说,调用它,只要告诉malloc多少个字节就可以直接分配内存;
  • 对于new来说,我们只要new一个对象即可;;
  • 其实还有一种是调用:: operator new的用法,这个::operator new还可以发生重载,并且使用它的方式也是给函数传入需要的字节数即可;但是对于::operator new的底层代码来说,也是调用了 malloc函数来的;


对于allocateor来说,不同的编译器的实现的接口有所不一样,这就使得我们有点讨厌;

  • 对于VC编译器来说:也是下图代码_MSC_VER那段;allocator()表示的是一个匿名对象,而int是分配的内存单元大小,allocator().allocate(3,(int*)0); allocate(3(int*)0);第一个参数表示分配了3个int大小的内存,至于第二个参数(int*)0,是一个无用的参数,至少对于现在来说,我们认为他是没有用的;
  • 对于——BORLANDC_的编译器来说:分配的使用方式也是和上面的vc差不多,但是没有第二个参数的设计,我门看上去觉得这似乎更加合理;我们知道vc低下的编译器和该编译器低下的释放内存,都需要指定指针和释放内存的大小,这就是有一把双刃剑,它很自由,但是假如你使用分配有错误的话,那么就会发生很严重的内存释放失败的问题;

对于GNU的编译器来说:其中下图的使用方式是GNU2.9的版本,但是不同的是,这个版本的调用方式是直接调用了allocate函数,也是但是分配过去不再是内存单元的大小,而是分配的字节大小了;
其实对于GNU4.9后的allocate分配器来说,书写方式和下图的方式不一样了;



GNU4.9的版本的allocateor的调用方式,其实GUN编译器有好多个分配器,下一显示的就是两种分配器,他们的用法都是通过对象去调用的;不再像2.9版本的GUN那样通过函数去调用;


4.基本构件之一new delete expression(上)


我们知道对于 new来说:是先分配内存,再调用构造函数,如上图,左边的部分,就是一个new的编译器的实现方式;由于要处理异常情况,再编译器实现new的过程中,会有一个try catch的异常处理;
并且值得关注的是,构造函数,是不可以自己调用的,而编译器而已自己调用
但是假如我们非要调用的话,那么就可以通过调用 placement new函数;至于 placement new是什么后面再讨论;
还有个注意的是:上面的 opreator new 是没有使用:: operator new的方式调用,我们知道::oprerator new 是可以被重载的,假如上面的类 Complex没有重载operator new 那么调用方式就是默认的全局那个::operator new 了;


接下来关注的是上图的右上角的部分,那个是operator new 在 vc98下的实现方式,我们要知道,不同编译器的实现可能都会有所差异,但是这个编译器的实现都可以代表了operator new的处理方式;

我们也知道,里面就是调用了底层的malloc去实现内存分配的;

我们关注opreator new的while循环里面;只要内存不足被分配失败了就会执行while循环里面的代码;
里面又调用了一个函数callnewh函数,这个函数主要是malloc内存分配失败后,去调用自己的callnewh函数,去释放你认为需要释放的内存字节,不必要的那些内存,这样,就可以再次进入循环,继续分配内存供你使用;


5.基本构件之一newdelete expression(中)

对于delete这个操作符来说:先析构再释放,在编译器底层也是调用了free来释放内存的。


7.Array new


我们知道,对于array new 一般都搭配 arrary delete使用的;但是我们有没有想过一个问题?
假如使用array new 分配内存,而不使用arrary delete释放内存呢,会发生什么?

其实我们大家都知道会发生内存泄漏,但是这个并不是一定的事情;而且内存泄漏到底在哪里发生内存泄漏可能也有人不清楚。
假如对于一个class来说,没有指针的成员,不是用arrary deleete是没有影响的;因为我们知道,delete的最终动作是先析构后释放内存,也就是说,假如你array new的分配内存,却不用 arrary delete的话,而用 delete就直接会析构一次,但是对于没有指针成员的class来说,即使你不析构也无所谓的,因为对象结束了,没有指针的成员数据也会跟着销毁,析构函数最主要是清理指针成员的堆区内存;
那么arrary delete比delete多出的动作也就是多次调用析构函数和一次调用析构函数的区别;
也就是说假如用arrary new 分配内存,用arrary delete释放内存,那么arrary会调用多次你在arrary new分配内存的元素的每个析构函数;
那么就很容易理解了:假如对于有指针成员的class来说:没有arrary delete的话,只会析构一次,也就是说,对于剩下的数组元素的对象,没有成功被析构,也就是说在这里才会发生内存泄漏;


我们其实需要知道,malloc实际分配内存来说,不单单只分配了你传入参数的字节数,其实还会多分配出一些cookie,这部分的内存是用来存放一些琐碎的内存信息;比如你用array new 分配内存,当你要释放内存时候,你是怎么知道array new到底分配了多少内存,这时候就需要有cookie来记录这些信息,其实对于数组来说,最主要的记录信息就是那个数组的长度了,到时候在释放内存的时候,就可以通过cookie里面记录的数组长度信息进行正确释放内存;


下面的测试主要说明:使用array分配内存时候和释放内存,是否调用了构造函数和析构函数;调用了多少次;
值得注意的是:要使用 array new 那么你的类中必须有默认构造函数,因为你在 new 时候,是无法有机会给它传参调用其他的构造构造的,那么只能通过有默认构造函数来完成对数据成员的初始化;


上面的测试结果:有一个是调用构造函数时从上往下,而析构时候是相反的;当然这个不一定所有编译器都是这样实现,不同的编译器的析构和构造顺序可能不一样;但是我们需要知道的是:能够正确分配和正确释放即可;


下图是使用array new 来分配基本数据类型int使用,释放却直接使用delete来释放的情况说明:
其实也是没有内存泄漏的,因为对于基本数据类型int来说,它这个根本不需要析构函数,也就没有所谓的内存泄漏的说法,释放内存也没什么错误;

我们分配上案例的结果字节大小是40,但是实际的内存分配的大小却不是,我们需要知道,右图是在vc低下的内存分配方式:观察又多出来的32字节还有12字节还有一些61h的东西,这些是什么,之后会谈;


下图的是有一个Demo类,大小为12个字节,并且还有析构函数,;我们也是使用array new去测试:发现分配3个元素,数组大小为36+4 = 40,这里多出的4个字节是做什么的呢?;
对于“有析构函数的类来说,用array new 分配内存多出了一块内存区域是4个字节记录了数组的大小从这个内存角度去解释也可以知道:假如用delete去释放 array new的时候,会发生错误,因为释放时候多出的记录数组大小的字节数不知道如如何释放了,不使用array delete打乱了释放内存的方式;其实本质就是里面的operator new 释放内存会错乱,并且里面的析构函数只析构一次,并不会发生什么问题,因为该Demo类中没有指针指向堆内存。

结论是对于类来说:有析构函数那么使用array new 分配内存就会多出4个字节记录大小,没有析构则不;


8.placement new


通过palcement new主动调用构造函数;
一般都是在一个类是用了array new 分配了内存后,需要对数组每个元素初始化,那么就可以通过 palcement new 的方式去调用该类的构造函数,达到初始化的目的;


9.重载

我们知道假如我们在应用程序写如下的代码红色框框的代码:编译器底层是绿色框框的方式实现的

假如我们没有自己重载 operator new 这个函数,那么编译器底层就会默认使用全局的::operator new 函数;也就是说,编译器会走如上图的2好路线;假如我们自己实现了一个operator new 那么就会走自己的operator new。


重载全局的::operator new 这种方式基本没人会这么干,因为重载全局的影响太大了,一旦你写得不好,就会很容易出大问题;


重载operator new 得基本格式


对于array new来说,operator new [ ]它的得重载格式如下


10.重载示例(上)

值得注意的是:重载局部的operator new时候我们都是要在类内加static;因为在创建对象时候,都没有对象,肯定不能通过对象来调用ioperator new 分配内存,所以说,只能设计static,但是实际上,就算不写static也是可以的,应该是编译器底层又默认实现了吧。



有一种很奇怪的写法:这种写法一般很少见,即这样写会跳过所有你重载的版本,直接调用的是全局的operator new;


11.重载示例(下)

当我们通过new()的方式去使用的时候,就是使用了operator new(size_t size,void* p);这个函数;
也就是说我们平时使用的:new(地址) ;这种方式底层就是把地址传给了operator new 函数的第二个参数;


***
通过下图的调用方式我们也发现:是真的只有在抛出异常时候,delete()的版本函数才会被调用;


总结来说重载:new(),其实就是第二个参数设计为其他的数据类型,只要不是void即可,因为设计为第二参数void的operator new 是 placement new的用法了;


我们可以看到,其实在标准库的string(真名是basic_string)类中,就有重载new()的用法,并且第二参数是 extra,类型为 size_t;


【侯捷】C++内存管理从平地到万丈高楼(前11节学习笔记)相关推荐

  1. 【C++内存管理-从平地起到万丈高楼】引言

    1.课程概要 从平地到万丈高楼--谁敢说自己的课程是万丈高楼呢?我敢!--侯老师,俺欣赏你!!!哈哈同学们的笔记和推荐的书: https://github.com/haotianmichael/Alg ...

  2. 侯捷 C++内存管理

    侯捷 C++内存管理课程目录: 第一讲:基础知识/工具 第二讲:malloc/free 第三讲:std::allocator 第四讲:other allocator 第五讲:loki::allocat ...

  3. 侯捷 C++内存管理 (一)

    本篇记录 <侯捷 C++内存管理 >,整理各节的要点,以备查阅 1.Overview 2.内存分配的每一层面 3.四个层面的基本用法 1).对比一下: 4.基本构件之一newdelete ...

  4. [侯捷 C++内存管理] 标准分配器实现

    [侯捷 C++内存管理] 标准分配器实现 文章目录 [侯捷 C++内存管理] 标准分配器实现 VC6 标准分配器之实现 VC6 malloc() VC6 allocator BC5 标准库分配器之实现 ...

  5. [侯捷C++](内存管理)

    文章目录 第一讲:primitives 四种内存分配与释放 基本构件之 new/delete expression 1.内存申请 2.内存释放 3.模拟编译器直接调用构造和析构函数 Array new ...

  6. [侯捷]C++ STL 体系结构与内核分析--从平地到万丈高楼(数据结构)

    01认识headers.版本.重要资源 泛型编程:即使用Template,而STL就是使用了泛型编程 重要资源,编程能用到reference 另一个网站,将STD的内容已经分为了六大类 另一个 p2 ...

  7. 11.FreeRTOS学习笔记-内存管理

    几种内存分配算法的比较 heap_1.c 管理方案是 FreeRTOS 提供所有内存管理方案中最简单的一个,它只能申请内存而不能进行内存释放,并且申请内存的时间是一个常量 heap_2.c方案支持释放 ...

  8. 《垃圾回收算法手册 自动内存管理的艺术》——标记整理与复制式回收(笔记)

    文章目录 三.标记-整理回收 3.1 双指针整理算法(任意顺序) 原理 步骤 3.2 Lisp 2算法(滑动顺序) 原理 步骤 3.3 引线整理算法(滑动顺序) 原理 步骤 3.4 单次遍历算法(滑动 ...

  9. Cocos2d-x for XNA:万丈高楼亦可不需平地起

    转自:http://www.th7.cn/Program/wp7/2012/06/05/79180.shtml 在今年的年初,Openxlive移植的cocos2d-x for WindowsPhon ...

最新文章

  1. Linux内核进程调度的时机和进程切换
  2. DAY9-字符串笔记整理2018-1-19
  3. plc模拟器软件_关于PLC虚拟化的思考当下及未来
  4. xml 文件解析 pull
  5. cvRemap 对图像进行普通几何变换
  6. 学生分组(洛谷P1109题题解,C++语言描述)
  7. 罗马音平假名片假名转换器_记不住五十音的你,你肯定需要这套日语五十音谐音巧记法...
  8. [技术博客] 微信小程序的formid获取
  9. 双缓冲(Double Buffer)原理和使用【转】
  10. FPGA的I2S采集数据处理
  11. 国产CAM究竟水平如何?看完测试我震惊了
  12. 【Virtual Box 错误】0x00000000 指令引用的 0x00000000 内存。该内存不能为written
  13. safari快捷图标不见了_桌面图标不见了怎么办?这里有妙招
  14. 2G,3G,4G,5G的不同之处
  15. 提醒用户的方式 notification+Dialog
  16. win10 查看版本信息(家庭版 专业版 企业版)
  17. gephi绘制红楼梦关系图
  18. 叶问【转自知数堂微信公众号】
  19. 在Textview中获取指定文字位置(兼顾网址链接和emoji表情),并在其附近展示图片
  20. 云服务器的购买及搭建

热门文章

  1. python股票代码示例_补全股票代码位数的一百种姿势
  2. java opencv 物体检测_OpenCV.物体识别
  3. 11月14日:jquery
  4. 看看下边的问题你能够回答出来多少?
  5. 阿里云部署vmware报错
  6. 如此优秀的你,字节跳动凭什么不给offer?
  7. OV强推性价比手机,小米或成最大受害者
  8. ListView实现倒计时功能
  9. 厌学孩子如何进行心理疏导[为本教育]
  10. 【Unity VR开发基础】Player视角设置调整与地面的相对高度