malloc — Динамическое размещение с использованием C ++ & quot; размещение нового & quot;

Вопрос: Как пользоваться «размещение нового«для создания массива с динамический размер? или, более конкретно, как выделить память для элементов массива из предварительно выделенной памяти.

Я использую следующий код:

void* void_array = malloc(sizeof(Int));
Int* final_array = new(void_array) Int;

Это гарантирует, что final_array * (указатель массива) будет выделен из места, которое зарезервировано void_array *. Но как насчет элементов final_array? Я хочу, чтобы они также выделялись из предварительно выделенной памяти.

P.SЯ должен сказать, что я использую некоторый API, который дает мне некоторый контроль над архитектурой плитки. Существует функция, которая работает точно так же, как malloc, но также имеет другие функции, например позволяет управлять свойствами выделенной памяти. Итак, что мне в основном нужно сделать, так это использовать эту функцию, подобную malloc, для выделения памяти с моими желаемыми свойствами (например, из какого банка памяти, где кэшироваться и т. Д.)

2

Решение

Прежде всего, давайте удостоверимся, что мы все согласны с разделением выделения памяти и конструирования объекта. Имея это в виду, давайте предположим, что у нас достаточно памяти для массива объектов:

void * mem = std::malloc(sizeof(Foo) * N);

Теперь вы не можете использовать новый массив размещения, потому что он сломан. Правильно сделать каждый элемент отдельно:

for (std::size_t i = 0; i != N; ++i)
{
new (static_cast<Foo*>(mem) + i) Foo;
}

(Приведение необходимо только для арифметики указателя. Фактический указатель, необходимый для размещения нового, является просто пустым указателем.)

Кстати, именно так работают контейнеры стандартной библиотеки и как устроены распределители стандартных библиотек. Дело в том, что вы уже знаем количество элементов, потому что вы использовали его в начальном распределении памяти. Следовательно, вам не нужно волшебство, предоставляемое массивом C ++.new, это все о хранении размера массива где-то и вызове конструкторов и деструкторов.

Уничтожение работает в обратном порядке:

for (std::size_t i = 0; i != N; ++i)
{
(static_cast<Foo*>(mem) + i)->~Foo();
}

std::free(mem);

Еще одна вещь, которую вы должен знать о, однако: исключение безопасности. Приведенный выше код на самом деле не является правильным, если Foo имеет конструктор без метания Чтобы правильно его кодировать, вы также должны сохранить место раскрутки:

std::size_t cur = 0;
try
{
for (std::size_t i = 0; i != N; ++i, ++cur)
{
new (static_cast<Foo*>(mem) + i) Foo;
}
}
catch (...)
{
for (std::size_t i = 0; i != cur; ++i)
{
(static_cast<Foo*>(mem) + i)->~Foo();
}
throw;
}
6

Другие решения

Вместо использования пользовательских mallocперезаписать operator new() и использовать это. Это не оператор new; есть функция на самом деле называется operator new()может показаться странным, что эта функция используется обычным оператором (без размещения) new для того, чтобы получить сырую память, на которой можно строить объекты. Конечно, вам нужно только перезаписать его, если вам нужно специальное управление памятью; в противном случае версия по умолчанию работает нормально.

Способ его использования заключается в следующем, если размер вашего массива будет size:

Int* final_array = static_cast<Int*>(size == 0 ? 0 : operator new(sizeof(Int) * size));

Тогда вы можете построить и уничтожить каждый элемент самостоятельно. Например, для элемента n:

// Create
new(final_array + n) Int; // use whatever constructor you want

// Destroy
(final_array + n)->~Int();
1

По вопросам рекламы [email protected]