Отказ от ответственности: Я не полностью тестировал этот код.
void* aligned_alloc(std::size_t size, std::size_t alignment){
if(alignment < alignof(void*)) {
alignment = alignof(void*);
}
std::size_t space = size + alignment - 1;
void* allocated_mem = ::operator new(space + sizeof(void*));
void* aligned_mem = static_cast<void*>(static_cast<char*>(allocated_mem) + sizeof(void*));
////////////// #1 ///////////////
std::align(alignment, size, aligned_mem, space);
////////////// #2 ///////////////
*(static_cast<void**>(aligned_mem) - 1) = allocated_mem;
////////////// #3 ///////////////
return aligned_mem;
}
void aligned_free(void* p) noexcept {
::operator delete(*(static_cast<void**>(p) - 1));
}
Объяснение:
Выравнивание доводят до alignof(void*)
если это меньше, чем это, потому что, как мы увидим, нам нужно хранить (выровнены) void*
.
Нам нужны size + alignment - 1
байт, чтобы гарантировать, что мы можем найти size
байт блок- там с выравниванием по правому краю, плюс дополнительные sizeof(void*)
байт для хранения указателя, возвращаемого ::operator new
, так что мы можем освободить его позже.
Мы выделяем эту память ::operator new
и сохраняем возвращенный указатель в allocated_mem
. Затем мы добавляем sizeof(void*)
байт в allocated_mem
и сохраняем результат в aligned_mem
. На данный момент мы еще не выровняли его.
В точке # 1, блок памяти и две точки выглядеть следующим образом:
aligned_mem (not actually aligned yet)
V
+-------------+-----------------------------------------+
|sizeof(void*)| size + alignment - 1 bytes |
+-------------+-----------------------------------------+
^
allocated_mem points here
std::align
вызова регулирует aligned_mem
для получения желаемого выравнивания. В точке # 2, это теперь выглядит следующим образом:
aligned_mem (correctly aligned now)
V
+---------------------+---------------------------------+
| extra space | at least size bytes |
+---------------------+---------------------------------+
^
allocated_mem points here
Поскольку мы начали в sizeof(void*)
байт мимо allocated_mem
, «лишнее пространство», по крайней мере sizeof(void*)
байт. Кроме того, aligned_mem
правильно выровнен для void*
, поэтому мы можем сохранить void*
прямо перед ним. В точке № 3, блок памяти выглядит следующим образом
aligned_mem (returned to caller)
V
+---------------+-----+---------------------------------+
| |^| at least size bytes |
+---------------+--+--+---------------------------------+
^ |
allocated_mem value of allocated_mem
points here stored here
Как aligned_free
, он просто читает указатель, хранящийся там, и передает его в ::operator delete
.
1) Не используйте 'new'. Это вызывает конструкторы. Используйте ':: operator new' для выделения памяти. Верните 'void *', не создавайте шаблон. 2) Вам нужно будет выделить дополнительную память для хранения исходного указателя, возвращаемого ':: operator new', чтобы впоследствии вы могли использовать его для освобождения. 3) Windows имеет '_aligned_malloc'. –
В чем преимущество возврата void *? – user1235183
'void *' дает понять, что вы возвращаете выделенное хранилище, а не построенные объекты. Это отдельные вещи, которые нужно обрабатывать отдельно. –