3

关于正确性,我找不到以下代码片段没有设计缺陷的证据/反驳。

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 的所有内容,这里本质上用作与提供的类型具有相同大小的内存袋。我不想在 中显式创建 Item 对象m_memoryBlock,因此我需要某种“具有足够空间的内存块”。

我可以确定 Chunk 的大小与提供的类型相同吗?请提供一些这种假设不起作用的例子。

如果我的假设完全错误,我应该如何处理?

4

1 回答 1

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任何标准内置类型适当对齐。


char另一种方法是使用 C++11alignas关键字使用您想要放置在该数组中的类型的精确对齐方式:

struct Chunk 
{
    alignas(Item) char mem[sizeof(Item)]; 
};

这正是std::aligned_storage对你有用的。

但是,这需要公开 的定义Item,这在某些情况下可能不方便或不受欢迎。

例如,此方法可用作Pimpl idiom的优化以避免内存分配,但是,这将需要在头文件中公开实现的定义以获得其大小和对齐方式,因此违背了 Pimpl 的目的。有关更多详细信息,请参阅The Fast Pimpl Idiom


另一个细节是,如果您想复制/移动Chunks 并期望存储的对象保持有效,则这些存储的对象必须是可简单复制的,例如

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>来说不是一个好的选择,因为在重新分配时,当向量增长时,所有指针和对存储在池中的对象的引用都会失效。

对象不应在通用内存池中移动。

于 2016-04-07T17:33:16.073 回答