我要构建一个自定义分配器,预先分配一个大块(数组)来存储某个类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
?
这个问题是否应该从不同的角度受到攻击?如果是这样,是什么?
答案 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 ++委员会已经有效地指定了分配器是引用类型:副本是等效的并且指向相同的内存池。
您可以使用实际嵌入到分配器中的内存池来避免作弊,但在某些实现中只能使用某些容器,并且您无法使用标准中的引用来支持它。