为什么在C ++ 11(涉及分配器)中交换标准库容器会有问题?

时间:2014-05-20 10:23:14

标签: c++ c++11 language-lawyer

注意: GreenScape最初要求comment


在阅读Why are the swap member functions in STL containers not declared noexcept?之后,似乎在为标准容器执行a.swap(b)时潜在的未定义行为的原因归结为也交换或不交换底层分配器。

  • 为什么交换分配器和数据有问题?

1 个答案:

答案 0 :(得分:13)

通过挖掘标准(N3797)来开始:

  

23.2.1p9 一般容器要求 [container.requirements.general]

     
    

如果     allocator_traits<allocator_type>::propagate_on_container_swap::value     是true,然后还应交换ab的分配器     使用对非成员swap的未命名调用。 否则,他们应该     不得交换,除非a.get_allocator() == b.get_allocator() ,否则行为未定义。

  

propagate_on_container_swap的目的是什么?

如果分配器有一个名为propagate_on_container_swap typedef ,它引用了std::true_type两个容器的基础分配器,那么它们也会交换。 [1] < / SUP>

如果propagate_on_container_swapstd::false_type,则只会交换两个容器的数据,但分配器将保留在原位。

[1] 这意味着在a.swap(b)之后,a.get_allocator()将是之前的b.get_allocator();分配器已交换


有状态分配器的含义是什么?

Allocator不仅负责为标准容器中的元素分配内存,它们还负责释放所述元素。

C ++ 03并不允许在标准容器中使用有状态分配器,但是C ++ 11要求必须支持这样的容器。这意味着我们可以定义一个分配器,它取决于它的构造方式,以某种方式起作用。

如果分配器的propagate_on_container_swap::value等于false,则所涉及的两个分配器之间的状态差异可能会导致未定义的行为,因为分配器的一个实例可能不是与另一方处理的数据兼容。


有状态分配器如果没有正确交换可能会出现什么问题?

我们假设我们有MagicAllocator使用mallocoperator new来分配内存,具体取决于它的构建方式。

如果它使用malloc分配内存,则必须使用free取消分配内存,如果operator new,则需要delete;因此,它必须保留一些信息,说明它应该使用哪两个。

如果我们有两个std::vector,它们都使用MagicAllocator但具有不同的状态(意味着一个使用malloc而另一个operator new),并且我们不会使用a.swap(b)。 t在free之后交换分配器,分配器在交换后不会匹配为两个向量中的元素分配的内存 - 这意味着错误的delete / {{1}}可能会被要求解除分配。