std :: align和std :: aligned_storage用于对齐的内存块分配

int*_*ull 9 c++ alignment allocator c++11

我试图分配的大小的存储器块size需要被Alignment对齐,其中尺寸可以在编译时定义.我知道例程,如_aligned_alloc,posix_memalign,_mm_alloc,等存在,但我不希望为他们带来降低代码的可移植性使用它们.
C++ 11给出了一个例行程序std::align,并且也是类std::aligned_storage从中我可以检索一个POD类型来分配将被对准以我的要求的元件.但是我的目标是创造条件,分配的内存块的分配size大小(不只是一个单一的元素),这将对齐.
这可能用std::align吗?我问的原因是std::align移动指针,使用该指针的类将给分配器一个指向移动的地址的指针,用于解除分配,这将是无效的.有没有办法以这种方式创建一个aligned_allocator?

Mat*_* M. 3

编辑:经过OP的澄清后,原来的答案似乎偏离了主题;出于参考目的,它保留在本答案的末尾。

实际上,答案相当简单:您只需要保留一个指向存储块和第一项的指针即可。

实际上,这并不需要有状态分配器(即使在 C++03 中也是可能的,尽管需要使用自定义std::align例程)。诀窍在于分配器不需要只向系统请求足够的内存来存储用户数据。它完全可以出于自己的簿记目的要求更多一点。

所以,我们在这里创建一个对齐的分配器;为了简单起见,我将重点关注分配/释放例程。

template <typename T>
class aligned_allocator {
    // Allocates block of memory:
    // - (opt) padding
    // - offset: ptrdiff_t
    // - T * n: T
    // - (opt) padding
public:
    typedef T* pointer;
    typedef size_t size_type;

    pointer allocate(size_type n);
    void deallocate(pointer p, size_type n);

}; // class aligned_allocator
Run Code Online (Sandbox Code Playgroud)

现在是分配例程。很多内存摆弄,毕竟它是分配器的核心!

template <typename T>
auto aligned_allocator<T>::allocate(size_type n) -> pointer {
    size_type const alignment = std::max(alignof(ptrdiff_t), alignof(T));
    size_type const object_size = sizeof(ptrdiff_t) + sizeof(T)*n;
    size_type const buffer_size = object_size + alignment;

    // block is correctly aligned for `ptrdiff_t` because `std::malloc` returns
    // memory correctly aligned for all built-ins types.
    void* const block = std::malloc(buffer_size);

    if (block == nullptr) { throw std::bad_alloc{}; }

    // find the start of the body by suitably aligning memory,
    // note that we reserve sufficient space for the header beforehand
    void* storage = reinterpret_cast<char*>(block) + sizeof(ptrdiff_t);
    size_t shift = buffer_size;

    void* const body = std::align(alignment, object_size, storage, shift);

    // reverse track to find where the offset field starts
    char* const offset = reinterpret_cast<char*>(body) - sizeof(ptrdiff_t);

    // store the value of the offset (ie, the result of body - block)
    *reinterpret_cast<ptrdiff_t*>(offset) = sizeof(ptrdiff_t) + shift;

    // finally return the start of the body
    return reinterpret_cast<ptrdiff_t>(body);
} // aligned_allocator<T>::allocate
Run Code Online (Sandbox Code Playgroud)

幸运的是,释放例程要简单得多,它只需要读取偏移量并应用它。

template <typename T>
void aligned_allocator<T>::deallocate(pointer p, size_type) {
    // find the offset field
    char const* header = reinterpret_cast<char*>(p) - sizeof(ptrdiff_t);

    // read its value
    ptrdiff_t const offset = *reinterpret_cast<ptrdiff_t*>(header);

    // apply it to find start of block
    void* const block = reinterpret_cast<char*>(p) - offset;

    // finally deallocate
    std::free(block);
} // aligned_allocator<T>::deallocate
Run Code Online (Sandbox Code Playgroud)

其他例程不需要知道内存布局,因此编写它们很简单。


原答案:

template <typename T>
class Block {
public:
    Block(Block const&) = delete;
    Block& operator=(Block const&) = delete;

    explicit Block(size_t n);
    ~Block();

private:
    void* _storage;
    T* _begin;
    T* _end;
}; // class Block

template <typename T>
Block<T>::Block(size_t n) {
    size_t const object_size = n * sizeof(T);
    size_t const buffer_size = object_size + alignof(T);

    _storage = std::malloc(size);

    void* stock = _storage;
    size_t shift = buffer_size;
    std::align(alignof(T), object_size, stock, shift);

    _begin = _end = reinterpret_cast<T*>(stock);
} // Block<T>::Block

template <typename T>
Block<T>::~Block() {
    for (; _end != _begin; --_end) {
        (_end - 1)->~T();
    }

    std::free(_storage);
} // Block<T>::~Block
Run Code Online (Sandbox Code Playgroud)