标签:
所有的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:
第{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}情况,操作依然保证移动的正确性。
源码阅读笔记 – std::vector (2) 关于Allocator Aware Container特性
标签:
原文地址:http://www.cnblogs.com/pointer-smq/p/5247422.html