在具有预分配块的自定义STL分配器中重新绑定

时间:2013-03-18 22:55:50

标签: c++ memory-management stl c++11 allocator

我要构建一个自定义分配器,预先分配一个大块(数组)来存储某个类N的{​​{1}}个元素,然后只增加一个索引在数组内部为服务分配请求。

由于我不希望对预分配块中的元素进行任何初始化,所以这样的东西不起作用:

T

因为在这种情况下,将为块的T buffer[N]; 元素调用T的构造函数。

由于我的理解是N没有调用std::aligned_storage的构造函数,我想到使用T,就像这样:

std::aligned_storage

然后,当请求T的分配时(直到std::aligned_storage< N * sizeof(T), std::alignment_of<T>::value >::type buffer; T* base = static_cast<T*>( static_cast<void*>(&buffer) ); ),分配器才能递增基指针,并且可以在适当的位置构造T (使用placement {{1在需要时。

我想使用此方案为STL容器定义自定义分配器。但是,在我看来,重新绑定可能存在问题。实际上,如果我的理解是正确的,那么STL分配器应该支持从类型(base+N)到类型new的重新绑定,例如,因为像T这样的容器(或其他基于节点的容器,如U)使用分配器来分配实际上不是std::list<T>类型的节点,但是类型不同std::map(包含T和节点的其他“标头”开销信息)。 那么,前面提到的U方法是否适用于重新绑定?或者(我认为)T s的正确对齐 是否意味着另一种不同类型std::aligned_storage的正确对齐?

怎样才能解决这个问题?

我如何定义前面提到的T以使其适用于重新绑定到某些不同类型的U

这个问题是否应该从不同的角度受到攻击?如果是这样,是什么?

1 个答案:

答案 0 :(得分:11)

你走在正确的轨道上。

一个令人恼火的细节是分配器的副本必须比较相等,甚至转换(反弹)副本。比较相等意味着他们可以释放彼此的指针。因此,std::list<int>之类的容器会将your_alloc<int>重新绑定到your_alloc<node<int>>,然后使用your_alloc<node<int>>构建your_alloc<int>。从技术上讲,your_alloc<node<int>>必须释放由your_alloc<int>分配的指针。

Here是我尝试满足这一要求的。随意复制/修改/滥用此代码。我的目的是教育,而不是成为世界的分配器供应商(无论如何都不会有利可图: - ))。

这个例子采用了略微不同的对齐方法:我碰巧知道在我感兴趣的平台(OS X,iOS)上malloc返回16字节对齐的内存,所以我的所有自定义分配器都需要返回。您可以将该数字更改为适合您系统的数字。

这种对齐的硬连线意味着单个池可以安全地提供多个allocator<int>allocator<node<int>>,因为它们都是16字节对齐的(这就足够了)。它还意味着副本,甚至是转换后的副本,可以检测指针何时指向缓冲区,因为副本都共享同一个缓冲区。

稍微不同的是,C ++委员会已经有效地指定了分配器是引用类型:副本是等效的并且指向相同的内存池。

您可以使用实际嵌入到分配器中的内存池来避免作弊,但在某些实现中只能使用某些容器,并且您无法使用标准中的引用来支持它。