Динамическое выравнивание памяти в C++11
posix_memalign
а также _aligned_malloc
в Windows позволяют динамически выделять выровненный кусок памяти. Есть ли что-нибудь подобное в C++11? Насколько я знаю, alignas
Ключевое слово работает только со статически размещенными объектами.
7 ответов
Это зависит от того, какое выравнивание вам требуется. Для чего угодно alignof(std::max_align_t)
, new
работает согласно n3242 3.7.4.1/2:
Возвращаемый указатель должен быть соответствующим образом выровнен, чтобы его можно было преобразовать в указатель любого полного типа объекта с фундаментальным требованием выравнивания
std::max_align_t
это полный тип объекта с самым строгим фундаментальным выравниванием.
Обратите внимание, что распределение массивов char
или же unsigned char
но нет signed char
есть другое правило в 5.3.4/10:
Для массивов char и unsigned char разница между результатом выражения new и адресом, возвращаемым функцией размещения, должна быть целым кратным самого строгого требования фундаментального выравнивания (3.11) любого типа объекта, размер которого не превышает размер создаваемого массива.
Так new char[1];
может иметь выравнивание 1.
Что касается выделения памяти с выравниванием больше, чем alignof(std::max_align_t)
, C++11 не предоставляет прямого способа сделать это. Единственный надежный способ - выделить как минимум size + alignment
байт и используйте std::align, чтобы получить правильно выровненное местоположение в этом буфере.
Это может тратить много памяти, поэтому, если вам нужно их много, вы можете создать распределитель, который выделяет достаточно большой блок для всех из них, и использовать для этого std::align. Затем ваши накладные расходы амортизируются по всем ассигнованиям.
Другой вариант - подождать, пока http://open-std.org/JTC1/SC22/WG21/docs/papers/2012/n3396.htm в стандарт.
Лично я бы просто написал слой абстракции поверх предоставляемых ОС API для выделения выровненной памяти.
Вы можете использовать posix_memalign/_aligned_malloc для выделения фрагмента памяти, а затем использовать специальный синтаксис оператора "new" для инициализации объекта в этой области памяти. Что-то вроде этого:
// Allocate raw memory for a Foo object.
void *mem;
size_t alignment = 0x1000;
size_t size = ?;
posix_memalign(&mem, alignment, size);
// Call the constructor on the allocated memory.
Foo *foo = new (mem) Foo(...);
// Now you have a useable object.
foo->some_method();
// Call destructor without freeing object memory.
foo->~Foo();
// Free raw memory.
free(foo);
Взгляни на std::aligned_storage
и alignas()
оператор. Они являются частью C++11 и, кажется, именно то, что вы ищете.
C++03 и C++0x имеют operator new
,
new T
или же new T[]
гарантирует возврат правильно выровненной памяти для объекта типа T.
new char[]
, new signed char[]
а также new unsigned char[]
гарантированно вернуть память правильно выровненную для любого объекта, так что вы можете использовать новое размещение на нем.
Для выровненной памяти, выделенной в куче, я использую функцию align() из http://code.google.com/p/c-plus/source/browse/src/util.h#57, потому что мой gcc4.8, похоже, не поддерживает Это. Вот пример кода:
typedef float TItem;
static const int SIZE = 100;
static const int ALIGNMENT = 16;
// allocate heap storage larger then SIZE
TItem* storage = new TItem[SIZE + (ALIGNMENT / sizeof(TItem))];
void* storage_ptr = (void*)storage;
size_t storage_size = sizeof(TItem) * (SIZE + 1);
// aligned_array should be properly aligned
TItem* aligned_array = (TItem*) align(MEM_ALIGNMENT, sizeof(TItem) * SIZE, storage_ptr, storage_size);
if (!aligned_array) { throw std::bad_alloc(); }
TBB от Intel предоставляет портативный cache_aligned_allocator
, который, я думаю, вы можете быть тем, что вы ищете.
Стандарт C++ всегда гарантировал подходящее выравнивание для любого объекта из распределений кучи, то есть
template<typename T> T* func() {
char* buf = new char[sizeof(T)];
return new(buf) T();
}
гарантированно не выйдет из строя по причинам выравнивания.