18

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

由于我希望对预分配块中的元素进行任何初始化,因此这样的事情将不起作用:

T buffer[N];

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

由于我的理解是std::aligned_storage不调用T构造函数,所以我想到了使用std::aligned_storage,如下所示:

std::aligned_storage<
    N * sizeof(T),
    std::alignment_of<T>::value 
>::type buffer;

T* base = static_cast<T*>( static_cast<void*>(&buffer) );

然后分配器可以在请求分配 T 时(直到(base+N))增加基指针,并且可以在需要时在适当的位置(使用放置new)构造 T。

我想使用这个方案为 STL 容器定义一个自定义分配器但是,在我看来, rebinding可能存在问题。事实上,如果我的理解是正确的,STL 分配器应该支持从 type 重新绑定T到 type U,例如因为容器std::list<T>(或其他基于节点的容器std::map)使用分配器来分配实际上不是 type 的节点T,但不同的节点类型U(包含T节点的其他“标头”开销信息)。那么,上述std::aligned_storage方法是否适用于重新绑定?或者(我认为)Ts的正确对齐不会暗示另一种不同类型的正确对齐U

如何解决这个问题?

我如何定义上述buffer内容以使其也适用于重新绑定到某些不同类型U

是否应该从不同的角度来解决这个问题?如果是这样,是什么?

4

1 回答 1

13

你在正确的轨道上。

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

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

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

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

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

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

于 2013-03-19T00:49:31.120 回答