栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

C++内存管理(2):分配内存的途径

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

C++内存管理(2):分配内存的途径

placement new

前面我们提到,不可以使用指针直接调用构造函数,这是编译器做的事情。

但如果想在已分配的空间上构造对象,就要使用placement new

具体用法如下:

这时已经new了一块空间,再使用new(buf)就可以在已有的空间上建立对象。

究其实现,其实调用placement new其实与new的步骤几乎无差别。

也分为调用operator_new,类型转换和调用构造函数。

而真正的区别在于重载了operator_new(),使其直接返回这块空间,相当于什么都没做。这样就可以实现直接使用构造函数的功能了。



分配内存的途径 应用程序

根据上述知识,我们在调用new和delete的时候,会调用到operator_new()和operator_delete()

operator_new()和operator_delete()都是可以重载的。

而如果使用全局的operator_new()和operator_delete()方法,一般很少重载(因为会影响全局),按照原生的operator_new()和operator_delete()来调用,那么就会调用到malloc和free

如果是在成员函数中调用operator_new()和operator_delete(),那么可以进行重载。

为什么要重载呢?可能设计者觉得malloc分配的效率不够高,可能觉得malloc的上下cookie比较浪费空间。

当然这些是否属实我们在研究malloc时就会清楚。

重载后,就可以按照自己的分配方式实现内存分配

最后我们就可以得到应用程序分配内存的两条途径:

容器

我们知道在STL中有分配器这样管理内存分配的角色。

而容器在使用分配器时,分配器的实现也有一些区别。

有些分配器使用了全局的operator_new()和operator_delete()来实现,也就是在底层调用malloc和free。

而有些分配器则根据自己的规则进行内存分配,如gnu中大名鼎鼎的alloc。



重载operator_new与operator_delete 全局

全局的operator_new()和operator_delete()方法重载需要注意,因为影响比较大,可能会出现一些意想不到的情况。所以一般不建议重载。

局部

局部重载一般发生在类中。

如果类对象调用new和delete,则会优先考虑局部重载的operator_new()和operator_delete()

重载时,标准的operator_new()中有size_t长度作为参数。

标准的operator_delete()中有void*表示指向这块空间的指针,size_t是长度(可选项)

重载时需要注意,这两个方法必须是静态方法。因为是在创建对象时调用,如果还没有对象那么非静态方法就无法调用。

operator_new[]和operator_delete[]

operator_new[]和operator_delete[]也可以重载,主要区别在于要根据数组的长度来增加分配空间的长度。



重载new()与delete()

这里说的重载new()与delete(),并不是我们前面的new与delete(要区分好)

我们可以在类中重载operator new(),但每个版本首先要保证参数列表不同,并且第一参数必须是size_t,其余参数出现在new()的()中。

这就与前面所提的placement new一致了。其实placement new就是一种重载实现。(当然这里是在类中,只能模拟标准库的实现)

而operator delete()的重载就不太一样了。

这些重载不会被delete调用,只有当重载的new在分配内存时抛出异常后,才会去找重载的delete(),看看有没有什么补救的办法。

重载后分配器的进化

因为operator_new()和operator_delete()可以重载,所以设计者们在实现自己的分配器上下起了功夫。

自定义分配器的思路就是:减少malloc的使用次数,从而减少cookie(因为malloc会分配上下cookie)。

类中的分配器1

既然想要减少malloc的调用,那么重载时应该先分配一大块空间,再进行切割。

这里的chunk就是一大块空间,然后一块一块串起来。

这样只会在分配大块空间时调用malloc,只有头尾两个cookie

通过测试可以看到,左边分配的内存空间间隔只有8,而右边使用全局的方法间隔则是16(因为每个对象都有上下cookie)

这里想要串起来,就需要next指针

而如果在类中增加一个next指针,那么就会增加4字节的空间,反而增加了消耗。

而在operator_delete中,是直接将要回收的块插到了链表的开头。

并没有真正delete,但不影响,因为还在掌控之中。

类中的分配器2

为了解决next指针占用空间的问题,在第二个版本中做了一些优化。

很巧妙地使用了联合体结构。也就是嵌入式指针

这种结构不影响本身的成员数据,但又可以在分配时用前4个字节当指针来进行串连。

最终也变成了链表形式,但没有额外的消耗。

静态分配器

当不同的class都在重载同样的方法时,设计者就在思考,能不能把它们抽出来,包装成一个大家都可以使用的class

这样就实现了一个静态类

allocate和deallocate方法与之前的类似。

在其他类中使用时,可以直接调用静态类的分配方法:

干净整洁。

用宏实现静态分配器

除了直接给出静态分配器,还有一种用宏实现的方式,稍作了解即可。

这样也可以在调用时使用宏

=default与=delete

我们在构造与析构时遇到过这两个关键字,其实在内存分配时也可以使用。

比如=delete就可以加在重载的分配函数后面,使其失效。

当然这样做也会运行失败。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/954731.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号