我无法找到以下代码段没有设计缺陷的证据/反证,说明正确性。
template <class Item>
class MyDirtyPool {
public:
template<typename ... Args>
std::size_t add(Args &&... args) {
if (!m_deletedComponentsIndexes.empty()) {
//reuse old block
size_t positionIndex = m_deletedComponentsIndexes.back();
m_deletedComponentsIndexes.pop_back();
void *block = static_cast<void *> (&m_memoryBlock[positionIndex]);
new(block) Item(std::forward<Args>(args) ...);
return positionIndex;
} else {
//not found, add new block
m_memoryBlock.emplace_back();
void *block = static_cast<void *> (&m_memoryBlock.back());
new(block) Item(std::forward<Args>(args) ...);
return m_memoryBlock.size() - 1;
}
}
//...all the other methods omitted
private:
struct Chunk {
char mem[sizeof(Item)]; //is this sane?
};
std::vector<Chunk> m_memoryBlock; //and this one too, safe?
std::deque<std::size_t> m_deletedComponentsIndexes;
};
我担心Chunk的所有东西,在这里使用的本质上是一个与提供的类型相同大小的内存包。
我不想在m_memoryBlock
中明确创建Item对象,因此我需要某种具有足够空间的&#34;大块内存&#34;。
我可以确定Chunk的大小与提供的类型相同吗?请提供一些例子,说明这个假设不起作用。
如果我的假设完全错了,我该如何处理?
答案 0 :(得分:4)
在这样的设计中,内存必须根据您想要在那里创建的对象类型进行适当的对齐。
标准内置类型通常具有自然对齐方式,等于其sizeof
,即sizeof(T) == alignof(T)
。
char
数组的对齐是1个字节,这对其他任何东西都不够。
强制对齐的一种简单方法是使用std::max_align_t
,如下所示:
union Chunk
{
std::max_align_t max_align;
char mem[sizeof(Item)];
};
这将使Chunk::mem
适合任何标准的内置类型。
另一种方法是使用C ++ 11 alignas
关键字在char
数组中使用您要放置的类型的精确对齐方式:
struct Chunk
{
alignas(Item) char mem[sizeof(Item)];
};
这正是std::aligned_storage
为你所做的。
然而,这需要暴露Item
的定义,这在某些情况下可能不方便或不合需要。
例如,此方法可以用作Pimpl idiom的优化以避免内存分配,但是,这需要在头文件中公开实现的定义以获得其大小和对齐,从而击败Pimpl的目的。有关详细信息,请参阅The Fast Pimpl Idiom。
另一个细节是,如果你想复制/移动Chunk
并期望存储的对象保持有效,那些存储的对象必须是trivially copyable,例如
static_assert(std::is_trivially_copyable<Item>::value,
"Item cannot be copied byte-wise");
typedef std::aligned_storage<sizeof(Item), alignof(Item)> Chunk;
对于内存池std::vector<Chunk>
不是一个好选择,因为在向量增长所有指针时重新分配,并且对存储在池中的对象的引用无效。
对象不应在通用内存池中移动。