3

我有一个关于 STL 类和分配器的问题,在网上似乎不容易找到。有谁知道嵌套 STL 类中使用了哪个分配器?例如:

typedef std::vector<int> myvect;

//下面的行已按照后续回复/评论所指出的那样进行了编辑

typedef std::map<int, myvect, std::less<int>, A> mymap; //uses my custom allocator for map creation

让我们调用默认分配器D,并假设我有一个自定义分配器A

如果我执行以下操作会发生什么:

  • 创建地图:

    mymap mapInstance;
    
  • 现在,假设存在一个条目mapInstance[0],假设我将一个值推入向量:

    mapInstance[0].push_back(999);
    

什么分配器用于向量的动态内存mapInstance[0]

到目前为止,我的理解D是使用了默认分配器,但我想确认A没有使用传递给地图的自定义分配器。(据我所知,这只会发生在我使用某种嵌套分配选项的情况下。)

当然,我知道元数据/标头信息mapInstance[0]是使用自定义分配器分配的A。我关心的是动态内存部分,也就是d_dataBegin.

4

2 回答 2

11

您的问题与Scoped Allocator Model相关,这是一种分配器设计风格,可自动将容器的分配器传播到容器的元素,这样您就可以确保容器的所有元素都是从同一个分配器分配的。更多关于下面的内容。

要回答您的问题:

1)容器默认不使用作用域分配器模型,你必须明确地请求它(见scoped_allocator_adaptor下文)

2)您的嵌套容器是类型std::vector<int>,这意味着它使用默认std::allocator<int>分配器,并且该类型的所有实例都是相等的,因此您的问题的答案是它使用标准分配器 - 哪个都没有关系,因为每个std::allocator<int>都是相同的。


这个答案的其余部分只是一个思想实验,你的问题的答案在上面:vector<int> 总是使用std::allocator<int>

现在,如果您的嵌套类型是自定义分配器在std::vector<int, A1<int>>哪里,A1<int>那么问题会变得更有趣。

嵌套容器将使用构建它的分配器,而您还没有展示这一点,因为您已经说过“假设存在一个条目mapInstance[0]”,并且该条目的创建方式决定了它将使用什么分配器。

如果该条目是这样创建的:

mapInstance[0];

那么该条目是默认构造的,并将使用默认构造的A1<int>

如果该条目是这样创建的:

A1<int> a1( /* args */ );
myvect v(a1)
mapInstance.insert(mymap::value_type(0, v));

那么条目将是的副本,v在 C++03 中,它的分配器将是 的副本v.get_allocator(),但在 C++11 中,它的分配器将是 的副本std::allocator_traits<A1<int>>::select_on_container_copy_construction(v.get_allocator()),它可能是的副本,v.get_allocator()但可能有所不同(例如默认构造的A1。)

(在 C++03 中,条目的分配器在创建后无法更改,因此答案将在此处结束,但在 C++11 中它可以被替换。我假设我们在这个问题的其余部分谈论 C++11 ,因为分配器在 C++03 中不是很有趣。)

如果该条目被这样修改:

A1<int> a1( /* args */ );
myvect v(a1)
mapInstance[0] = v;

然后向量被复制分配到,这可能会替换分配器,具体取决于std::allocator_traits<A1<int>>::propagate_on_container_copy_assignment::value

如果该条目被这样修改:

A1<int> a1( /* args */ );
mapInstance[0] = myvect(a1);

然后向量被移动分配到,这可能会替换分配器,具体取决于std::allocator_traits<A1<int>>::propagate_on_container_move_assignment::value

如果该条目被这样修改:

A1<int> a1( /* args */ );
myvect v(a1)
swap( mapInstance[0], v );

然后向量被交换,这可能会替换分配器,具体取决于std::allocator_traits<A1<int>>::propagate_on_container_swap::value


现在,如果A事情std::scoped_allocator_adaptor<A1<std::pair<const int, myvect>>>变得更加有趣!scoped_allocator_adaptor顾名思义,它是一个适配器,它允许任何分配器类型与 Scoped Allocator Model 一起使用,这意味着容器的分配器可以传递给容器的子代,以及它的子代的子代等(只要这些类型使用分配器并且可以用它们构造。)

默认情况下,容器和分配器使用您需要使用的作用域分配器模型scoped_allocator_adaptor(或编写您自己的工作方式相同的分配器类型)来使用它。(而且 C++03 根本不支持作用域分配器。)

如果该条目是这样创建的:

mapInstance[0];

然后不是默认构造的条目,scoped_allocator_adaptor而是使用映射分配器的副本构造它,因此条目将像myvect( A1<int>(mapInstance.get_allocator()) ).

如果该条目是这样创建的:

A1<int> a1( /* args */ );
myvect v(a1)
mapInstance.insert(mymap::value_type(0, v));

那么该条目将拥有v的数据的副本,但不会使用其分配器,而是由 传递一个分配器scoped_allocator_adaptor,因此将被构造为:myvect( v, A1<int>(mapInstance.get_allocator()) )


如果这有点令人困惑,欢迎来到我的世界,但别担心,在你的情况下vector<int>始终使用std::allocator<int>.

于 2012-11-08T22:20:50.043 回答
1

默认分配器D用于push_back调用。

实际上,默认分配器也被使用map,因为容器的类型指定使用默认分配器。假设您A继承自std::allocator,所发生的一切就是您的分配器被切片为默认分配器,并且容器的行为就像您根本没有传递分配器实例一样。

于 2012-10-03T04:15:58.870 回答