源码阅读笔记 - 2 std::vector (2) 关于Allocator Aware Container特性
所有的STL容器,都保存一个或默认,或由用户提供的allocator的实例,用来提供对象内存分配和构造的方法(除了std::array),这样的容器,被称作Allocator Aware Container。早期的STL,设计的尚不完善,各种实现之间不能相互兼容,这一点在侯捷的《STL源码剖析》中有提到:有些STL的实现无法兼容标准的allocator实现,因为他们使用了更为复杂的二级配置器。而在我昨天阅读完vector和其处理allocator拷贝、移动和交换问题的源码后,发现这种问题在如今的STL中已经不再存在,allocator和容器的标准本身提供了选项,用于更加复杂的分配器的拷贝操作。
每一个Allocator Aware Container在拷贝构造(copy constuction)的时候,都会调用被拷贝对象的std::allocator_traits<TAllocator>::select_on_container_copy_construction()函数,这是一个静态函数,会通过编译时重载决议,按情况拷贝,或者直接返回被拷贝对象的allocator。
函数内部通过调用allocator的成员select_on_container_copy_construction()函数,如果没有,就直接返回容器本体。
同时,容器的设计者也应该在容器拷贝的时候要么调用静态的propagate_on_container_copy_assignment,要么直接调用成员propagate_on_container_copy_assignemnt,获取被拷贝容器的allocator副本,避免出现直接拷贝容器的bug。
每一个std::allocator_traits<Tallocator>都拥有三个别名类型:propagate_on_container_copy_assignment, propagate_on_container_move_assignment 和 propagate_on_container_swap,他们都是true_type或false_type的别名,这三个属性除非用户自定义,否则默认是false_type,也即allocator在容器拷贝、移动或交换的时候不能直接进行allocator所分配的内存的所有权的转移
容器在移动赋值(move assignment)的时候需要考虑如下情况,来正确操作容器的allocator:
- propagate_on_container_move_assignemnt 为 true_type
- propagate_on_container_move_assignemnt 为 false_type,但两个allocator相等
- propagete_on_container_move_assignment 为 false_type,两个allocator不等
第{1}和{2, 3}能通过编译时重载决议区分,而{2},{3}需要运行时通过if判断。
第一种情况下,lhs需要先用他自己的allocator释放掉它自己分配的东西,然后rhs的allocator的所有权转移(move),最后是memory的所有权从rhs转移到lhs。
第二种情况下,可以重复第一种情况,但是allocator本身不需要交换所有权
第三种情况,无法执行内存级别的移动,只能进行对象级别的移动
对于移动构造,直接把allocator move过来,然后转移memory的所有权。
对于拷贝赋值(copy assignment),需要运行时判断容器的propagate_on_copy_assignement trait,如果为true,并且两个容器不相等,那么lhs的容器应该先析构所有内存,再拷贝allocator,最后执行对象的拷贝。
有了上面的设计,使用多级分配器的容器间的拷贝变得有可能了,如果有人想要给每一个allocator使用一个独立的内存池,那么显然内存池之间的对象所有权不是随随便便拷贝个指针就能转移的,这时也许需要把三个tag都设置为false,让allocator的operator==重载不直接返回true,使得资源的转移按照自己期望的方式进行。如果自定义allocator内存池可以直接进行已分配对象的内存池间所有权传递,那么propagate_on_container_move_assignemnt可以为true_type,然后在两个内存池allocator的移动构造函数里做好所有权转移的事情;如果自定义allocator内存池不能转移已分配对象的所有权,那就把propagate_on_container_move_assignemnt设为false,operator==中进行两个内存池的比较,这时候回到上面说的{2, 3}情况,操作依然保证移动的正确性。
源码阅读笔记 - 2 std::vector (2) 关于Allocator Aware Container特性的更多相关文章
- 源码阅读笔记 - 2 std::vector (1)
vector的源码真是太长了,今天用了一个下午和一个晚上看和注释了前面的一千行左右 p.s.博客园的代码高亮真是太垃圾, 如果想要阅读带注释的源码,推荐粘贴到VS2015里,然后按ctrl+z取消自动 ...
- 源码阅读笔记 - 3 std::string 与 Short String Optimization
众所周知,大部分情况下,操作一个自动(栈)变量的速度是比操作一个堆上的值的速度快的.然而,栈数组的大小是在编译时确定的(不要说 C99 的VLA,那货的 sizeof 是运行时计算的),但是堆数组的大 ...
- 源码阅读笔记 - 1 MSVC2015中的std::sort
大约寒假开始的时候我就已经把std::sort的源码阅读完毕并理解其中的做法了,到了寒假结尾,姑且把它写出来 这是我的第一篇源码阅读笔记,以后会发更多的,包括算法和库实现,源码会按照我自己的代码风格格 ...
- mxnet源码阅读笔记之include
写在前面 mxnet代码的规范性比Caffe2要好,看起来核心代码量也小很多,但由于对dmlc其它库的依赖太强,代码的独立性并不好.依赖的第三方库包括: cub dlpack dmlc-core go ...
- CI框架源码阅读笔记5 基准测试 BenchMark.php
上一篇博客(CI框架源码阅读笔记4 引导文件CodeIgniter.php)中,我们已经看到:CI中核心流程的核心功能都是由不同的组件来完成的.这些组件类似于一个一个单独的模块,不同的模块完成不同的功 ...
- CI框架源码阅读笔记4 引导文件CodeIgniter.php
到了这里,终于进入CI框架的核心了.既然是“引导”文件,那么就是对用户的请求.参数等做相应的导向,让用户请求和数据流按照正确的线路各就各位.例如,用户的请求url: http://you.host.c ...
- CI框架源码阅读笔记3 全局函数Common.php
从本篇开始,将深入CI框架的内部,一步步去探索这个框架的实现.结构和设计. Common.php文件定义了一系列的全局函数(一般来说,全局函数具有最高的加载优先权,因此大多数的框架中BootStrap ...
- CI框架源码阅读笔记2 一切的入口 index.php
上一节(CI框架源码阅读笔记1 - 环境准备.基本术语和框架流程)中,我们提到了CI框架的基本流程,这里再次贴出流程图,以备参考: 作为CI框架的入口文件,源码阅读,自然由此开始.在源码阅读的过程中, ...
- Three.js源码阅读笔记-5
Core::Ray 该类用来表示空间中的“射线”,主要用来进行碰撞检测. THREE.Ray = function ( origin, direction ) { this.origin = ( or ...
随机推荐
- 手动刷新magento的索引管理方法
当我们网站商品很多的时候,比如有几千件,我们刷新Magento的索引管理(Index Management)经常会失败.那么后台刷新不了,我们还可以通过命令行来刷新. 使用命令行来刷新索引管理会极大降 ...
- REDIS fdatasync技术问题和BIO技术的引入
http://oldblog.antirez.com/post/fsync-different-thread-useless.html 这是原文作者的博客 把他翻译下 带上自己的一些理解 看看作者引入 ...
- 《深入浅出Node.js》第8章 构建Web应用
@by Ruth92(转载请注明出处) 第8章 构建Web应用 一.基础功能 请求方法:GET.POST.HEAD.DELETE.PUT.CONNECT GET /path?foo=bar HTTP/ ...
- Largest Rectangle in Histogram
Given n non-negative integers representing the histogram's bar height where the width of each bar is ...
- java 中List.subList 总结
今天,维护以前的代码,看到了List.subList这个方法,以前没接触过,对这个就是个小白,今天学习下: java.util.List中有一个subList方法,用来返回一个list的一部分的视图. ...
- JS和Android交互
//本地webview写法 webview = (WebView) findViewById(R.id.webview); webview.loadUrl("http://192.168.1 ...
- 关于Kendo的Grid 单元格样式
<!DOCTYPE html><html style="height: 100%;"><head><meta http-equiv=&qu ...
- Spark RDD
对RDD的学习进行一下整理 RDD:基于内存的集群计算容错抽象 分布式内存抽象的概念---弹性分布式数据集(RDD),它具备MapReduce等数据流模型的容错特性,并且允许开发人员在大型集群上执行基 ...
- Java中的抽象类
含有抽象方法的类,抽象方法即用abstract修饰的方法,即父类只知道其子类应该含有该方法,但无法知道子类如何实现这些方法 抽象类限制规定子类必须实现某些方法,但不关注实现细节 抽象类中可以包含普通方 ...
- coderforces #387 Servers(模拟)
Servers time limit per test 2 seconds memory limit per test 256 megabytes input standard input outpu ...