Страницы

Поиск по вопросам

пятница, 19 октября 2018 г.

Выделение нетипизированной памяти для массива произвольного типа

Если я выделяю память для некоторого типа T - пусть это в шаблоне, скажем -
template char* alloc(size_t n) { return new char[n*sizeof(T)] ; }
то места гарантированно хватит для n элементов с типом T или нет? Какие-то проблемы из-за выравнивания могут быть? И если могут - то как их правильно решить?
Дополнение - по совету @VladD я ппробовал выделить память с особыми требованиями к выравниванию:
#include #include
using namespace std;
struct alignas(256) D { char a; char b; };
template void* alloc(size_t n) { return reinterpret_cast(new aligned_storage[n]); }
template int allocSize() { cout << "sizeof = " << sizeof(T) << ", alignof = " << alignof( T) << endl; cout << "type = " << typeid(typename aligned_storage::type).name() << endl; return sizeof(typename aligned_storage::type); }
int main() { cout << allocSize() << endl; cout << alloc(5) << endl; }
GCC это не смог отработать - https://ideone.com/dgo75h. Но Visual Studio нормально отработал, но выдал:
sizeof = 256, alignof = 256 type = union std::_Align_type 256 004A1258
т.е. понял и размер, и выравнивание, но адрес дал выровненный явно не на 256. Мне это выравнивание не нужно, это так, игры - но тем не менее получается, что так выделять память не получается?


Ответ

По идее, вам нужно std::aligned_storage
Должно по идее работать так:
template char* alloc(size_t n) { return new aligned_storage[n]; }

Проблемы из-за выравнивания с простым подходом очевидны: new char[] не знает ваших требований по выравниванию, и может выделить неправильно выровненную память.

Для выделения объектов в вашей памяти вам понадобится placement new. Вам должен пригодится std::align, использование которого описано здесь

Обновление: я перепроверил, и на самом деле new не соблюдает кастомные требования по выравниванию. Поэтому нужно делать выравнивание самостоятельно, при помощи std::align. Получается вот что:
// подсчёт длины нужной аллокации template size_t aligned_char_size(size_t n) { size_t result = n * sizeof(T); bool is_overaligned = alignof(T) > alignof(std::max_align_t); if (is_overaligned) result += alignof(T) - 1; return result; }
Применяем:
struct alignas(1024) S { };

// выделёем память на 3 экземпляра size_t total_space = aligned_char_size(3); void* p = new char[total_space]; std::cout << p << std::endl;
// получаем выровненный указатель при помощи std::align void* allocated_item = p; size_t remaining_space = aligned_char_size(3); if (std::align(alignof(S), sizeof(S), allocated_item, remaining_space)) { std::cout << allocated_item << std::endl; std::cout << (size_t)allocated_item % alignof(S) << std::endl; } else { std::cout << "Impossible" << std::endl; }
Результат на Visual Studio 2017:
000002333DF15950 000002333DF15C00 0
Если убрать alignas(1024), получаем
000002A27FD2ECD0 000002A27FD2ECD0 0

Комментариев нет:

Отправить комментарий