5

当我通过 Allocator 学习 STL 时,我在互联网上找到了很多东西,我或多或少地了解了它们的作用。我看过一个自定义分配器来了解它们是如何实现的,但我的问题是容器如何使用它们,比如说 Vector ?

他们总是说向量正在使用默认分配器。例如,空向量构造函数是:

   explicit vector (const allocator& alloc = allocator());

但是 vector 如何使用分配器?

幕后的向量何时何地使用 Allocator::allocate() 和 Allocator::construct() ?

知道了,为什么需要自定义分配器,它有什么变化?

4

3 回答 3

2

不同的编译器使用不同的 STL 实现,由这些实现决定它们如何使用分配器。这可能会导致一些不幸的结果。

例如,我主要使用基于 ARM 架构的嵌入式系统,并且在某一时刻,我尝试实现自己的基于固定大小内存块的分配器,用于与listmapset(我没想到它适用于vector,因为足够大的向量会超过块的大小)。在这个过程中,我发现 ARM 编译器 v3.1 使用了 STL 的 RogueWave 实现版本,其中一些容器对分配器的行为做出了某些假设。在 和 的情况下mapset例如:

  • 它从来没有调用过Allocator::max_size()——它假设它返回了足够大的东西,它不必担心它。
  • 它假设这allocate()很慢,并尝试通过一次分配多个对象来优化它(vector旨在做到这一点,但我没想到同样的行为也适用于不使用连续内存的容器mapset。基本上,它不是调用allocate(sizeof(T)),而是调用allocate(sizeof(T) * 20)来获取一个内存池,然后在创建新对象时可以在内部重新分配。

这些假设几乎破坏了我在该项目中使用 STL 的尝试——我最终改用boost::intrusive容器。

于 2013-09-19T18:06:34.673 回答
2

正如@BenjaminLindley 提到的,有很多地方可以在容器内使用分配器。一个例子是push_back它使用分配器特征在容器内构造其参数的副本

标准(23.2.3)描述push_back为:

a.push_back(t)

附加 t 的副本。要求:T 应可复制插入到 X 中。

后来,术语 CopyInsertable 被定义为:

T is CopyInsertable into X 意味着,除了 T 是 MoveInsertable into X 之外,以下表达式是格式良好的:

allocator_traits<A>::construct(a, p, t)

...可以变成:

a.construct(p, t)

...它有效地使用分配器来构造at locationa的副本。tp

于 2013-09-19T17:36:46.180 回答
0

关于“知道,为什么需要自定义分配器,它有什么变化?”

  • 自定义分配器可以优化小对象的分配
  • 优化具有共同大小的对象的分配
  • 从内存中挤出最后一点
  • ...

默认的只有一种策略,只有(可能)。

于 2013-09-19T18:59:43.513 回答