所有的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特性的更多相关文章

  1. 源码阅读笔记 - 2 std::vector (1)

    vector的源码真是太长了,今天用了一个下午和一个晚上看和注释了前面的一千行左右 p.s.博客园的代码高亮真是太垃圾, 如果想要阅读带注释的源码,推荐粘贴到VS2015里,然后按ctrl+z取消自动 ...

  2. 源码阅读笔记 - 3 std::string 与 Short String Optimization

    众所周知,大部分情况下,操作一个自动(栈)变量的速度是比操作一个堆上的值的速度快的.然而,栈数组的大小是在编译时确定的(不要说 C99 的VLA,那货的 sizeof 是运行时计算的),但是堆数组的大 ...

  3. 源码阅读笔记 - 1 MSVC2015中的std::sort

    大约寒假开始的时候我就已经把std::sort的源码阅读完毕并理解其中的做法了,到了寒假结尾,姑且把它写出来 这是我的第一篇源码阅读笔记,以后会发更多的,包括算法和库实现,源码会按照我自己的代码风格格 ...

  4. mxnet源码阅读笔记之include

    写在前面 mxnet代码的规范性比Caffe2要好,看起来核心代码量也小很多,但由于对dmlc其它库的依赖太强,代码的独立性并不好.依赖的第三方库包括: cub dlpack dmlc-core go ...

  5. CI框架源码阅读笔记5 基准测试 BenchMark.php

    上一篇博客(CI框架源码阅读笔记4 引导文件CodeIgniter.php)中,我们已经看到:CI中核心流程的核心功能都是由不同的组件来完成的.这些组件类似于一个一个单独的模块,不同的模块完成不同的功 ...

  6. CI框架源码阅读笔记4 引导文件CodeIgniter.php

    到了这里,终于进入CI框架的核心了.既然是“引导”文件,那么就是对用户的请求.参数等做相应的导向,让用户请求和数据流按照正确的线路各就各位.例如,用户的请求url: http://you.host.c ...

  7. CI框架源码阅读笔记3 全局函数Common.php

    从本篇开始,将深入CI框架的内部,一步步去探索这个框架的实现.结构和设计. Common.php文件定义了一系列的全局函数(一般来说,全局函数具有最高的加载优先权,因此大多数的框架中BootStrap ...

  8. CI框架源码阅读笔记2 一切的入口 index.php

    上一节(CI框架源码阅读笔记1 - 环境准备.基本术语和框架流程)中,我们提到了CI框架的基本流程,这里再次贴出流程图,以备参考: 作为CI框架的入口文件,源码阅读,自然由此开始.在源码阅读的过程中, ...

  9. Three.js源码阅读笔记-5

    Core::Ray 该类用来表示空间中的“射线”,主要用来进行碰撞检测. THREE.Ray = function ( origin, direction ) { this.origin = ( or ...

随机推荐

  1. 开源框架中常用的php函数

    类的自动加载后直接实例化 //自动加载类 function my_autoloader($class) { include $class . 'Class.php'; } spl_autoload_r ...

  2. jquery 操作

    Jquery使用时要引用,引用时放在最前. Jquery: $代表选择器, $(document) ready(function(e){}):找到页面,页面加载完成后执行. JS选取元素操作内容操作属 ...

  3. 基于数据库MySQL的简易学生信息管理系统

    通过这几天学习Mysql数据库,对其也有了基本的了解,为了加深印象,于是就写了一个最简易的学生信息管理系统. 一:基本要求 1.通过已知用户名和密码进行登录: 2.可以显示菜单: 3.可以随时插入学生 ...

  4. VB 中Sub和Function的区别

    Sub可以理解为执行一个过车,一个操作. Function在执行完过后,还要返回一个结果. Sub:过程:Function:函数,可以带返回值. 语法: Sub SubName(参数1,参数2,... ...

  5. 快速上手RaphaelJS--Instant RaphaelJS Starter翻译(三)

    (目前发现一些文章被盗用的情况,我们将在每篇文章前面添加原文地址,本文源地址:http://www.cnblogs.com/idealer3d/p/Instant_RaphaelJS_Starter3 ...

  6. thinkPHP CRUD操作

    数据访问 以 nation 表为例 方法一  => select() ①造模型对象 $naiton = D('Nation');   //也可以使用M()方法 ②查询所有 $a = $natio ...

  7. MSP430单片机的两种SPI总线实现方式

    MSP430单片机上的SPI总线的实现方式分为两种:硬件实现和软件实现. 二者的抽象层次不同,硬件实现方式下程序员只需要完成总线协议的寄存器层,即一字节(char,8位二进制)数据,而软件实现方式下程 ...

  8. java.nio.ByteBuffer中flip,rewind,clear方法的区别

    对缓冲区的读写操作首先要知道缓冲区的下限.上限和当前位置.下面这些变量的值对Buffer类中的某些操作有着至关重要的作用: limit:所有对Buffer读写操作都会以limit变量的值作为上限. p ...

  9. gulp学习笔记

    第一步:安装Node 首先,gulp 是基于 Nodejs 的自动任务运行器,所以安装gulp之前,最基本也最重要的是,我们需要搭建node环境.访问http://nodejs.org,下载并安装No ...

  10. poj2368 Buttons Nim取石子游戏

    链接:http://poj.org/problem?id=2368 和前面差距还是很大啊囧 代码: k,a;main(i){,i=;i<=k/&&k%i;++i);k%i||(a ...