【问题标题】:Which allocator is used when allocating a nested STL container?分配嵌套 STL 容器时使用哪个分配器?
【发布时间】:2012-09-23 22:50:35
【问题描述】:

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

typedef std::vector<int> myvect;

//下面的行已按照后续回复/cmets的指出进行了编辑

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之后的部分。

【问题讨论】:

  • 首先要知道的是,在标准库中,分配器改变了容器的类型(它是容器的模板参数),而默认分配器是无状态的,所以 exact您的示例中使用的分配器的副本不会产生影响。
  • d_dataBegin 是什么?!直到问题的最后一句话似乎没有在任何地方提及!

标签: c++ stl map vector allocator


【解决方案1】:

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

回答你的问题:

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

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


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

现在,如果您的嵌套类型是 std::vector&lt;int, A1&lt;int&gt;&gt;,其中 A1&lt;int&gt; 是自定义分配器,那么问题会变得更有趣。

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

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

mapInstance[0];

那么条目是默认构造的,将使用默认构造的A1&lt;int&gt;

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

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&lt;A1&lt;int&gt;&gt;::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&lt;A1&lt;int&gt;&gt;::propagate_on_container_copy_assignment::value的值

如果该条目被这样修改:

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

然后向量被移动分配到,可能替换分配器,具体取决于std::allocator_traits&lt;A1&lt;int&gt;&gt;::propagate_on_container_move_assignment::value的值

如果该条目被这样修改:

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

然后向量被交换,这可能替换分配器,具体取决于std::allocator_traits&lt;A1&lt;int&gt;&gt;::propagate_on_container_swap::value的值


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

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

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

mapInstance[0];

那么scoped_allocator_adaptor 将使用映射分配器的副本而不是默认构造的条目来构造它,因此条目将像myvect( A1&lt;int&gt;(mapInstance.get_allocator()) ) 一样构造。

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

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

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


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

【讨论】:

    【解决方案2】:

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

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

    【讨论】:

    • 我不太明白那个标记——我编辑了我的原始问题以正确地将分配器传递给地图,但我没有得到你谈论 A 被切片为默认值的部分一个容器的行为就像我根本没有传递分配器实例一样。假设分配器 A 内置了一些特殊的内存调试。如果我按照我在原始问题中的编辑使用它来创建 A,那么您所说的是否适用(并且只使用 D?...我不太同意这一点,或者我遗漏了一些东西)。关于用于 push_back 调用的 D,我同意。谢谢!
    • 为了其他读者的利益,当写这个答案时,问题使用了std::map&lt;int, myvect&gt; mapInstance(A),所以 Mark B 是正确的,该映射使用了默认分配器 - 问题已被编辑,所以这个答案现在看起来是错误的不过。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-07
    • 2016-06-07
    • 1970-01-01
    • 2011-07-14
    相关资源
    最近更新 更多