Динамическое выравнивание памяти в 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();
}

гарантированно не выйдет из строя по причинам выравнивания.

Другие вопросы по тегам