我正在尝试分配一块大小的内存,size
该内存块需要Alignment
在编译时可能未定义大小的地方对齐。我知道存在诸如_aligned_alloc
, posix_memalign
,_mm_alloc
等例程,但我不想使用它们,因为它们降低了代码的可移植性。
C++11提供了一个例程std::align
和一个类std::aligned_storage
,我可以从中检索 POD 类型来分配一个符合我要求的元素。但是我的目标是创建一个分配器,它将分配一块size
大小为对齐的内存块(不仅仅是一个元素)。
这可以使用std::align
吗?我问的原因是因为std::align
移动指针,使用该指针的类将给分配器一个指向移动地址的指针以进行释放,这将是无效的。有没有办法以这种方式创建对齐分配器?
问问题
6281 次
2 回答
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
现在分配例程。大量的内存摆弄,毕竟它是分配器的核心!
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
幸运的是,释放程序要简单得多,它只需要读取偏移量并应用它。
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
其他例程不需要知道内存布局,因此编写它们很简单。
原答案:
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
于 2013-06-29T13:53:22.517 回答
1
如果它必须是 C++11 解决方案,请忽略此答案。
如果不是...我不知道您是否已经知道这一点,但这是一种选择:
void * aligned_malloc( size_t size, size_t alignement )
{
void * p = malloc( size + --alignement );
void * p1 = (void*)( ( (size_t)p + alignement ) & ~alignement );
((char*)p1)[ -1 ] = (char)((char*)p1 - (char*)p);
return p1;
}
void aligned_free( void * pMem )
{
char * pDelete = (char*)pMem - ((char*)pMem)[ -1 ];
free( pDelete );
}
也许malloc
并且free
不是 100% 可移植的,但是使用预处理器指令很容易处理这种情况。
于 2013-06-29T13:40:49.410 回答