Ответ 1
Это до реализации. Выделение и освобождение памяти не является "наблюдаемым поведением", если только реализация не решит, что это наблюдаемое поведение.
На практике ваша реализация, вероятно, связана с некоторой библиотекой времени выполнения С++, и когда ваш TU скомпилирован, компилятор вынужден распознавать, что вызовы в эту библиотеку могут иметь наблюдаемые эффекты. Насколько я знаю, это не соответствует стандарту, это просто то, как все нормально работает. Если оптимизатор может каким-то образом решить, что определенные вызовы или комбинации вызовов фактически не влияют на наблюдаемое поведение, тогда он может их удалить, поэтому я считаю, что специальный случай, чтобы определить ваш примерный код и удалить его, будет соответствовать.
Кроме того, я не помню, как работает пользовательский глобальный [мне напомнили]. Поскольку код может вызывать определения этих вещей в другом пользовательском TU, который позже связан с этим TU, вызовы не могут быть оптимизированы во время компиляции. Они могут быть удалены во время соединения, если выяснится, что операторы не определены пользователем (хотя тогда применяются материалы о библиотеке времени выполнения) или определяются пользователем, но не имеют побочных эффектов (как только пара из них - это кажется довольно неправдоподобным в разумной реализации, фактически [*]).new[]
и delete[]
Я уверен, что вам не разрешено полагаться на исключение из new[]
, чтобы "доказать", закончилось ли у вас нехватка памяти. Другими словами, только потому, что new char[10]
не выбрасывает это время, не означает, что он не будет бросать после того, как вы освободите память и повторите попытку. И только потому, что он бросил последний раз, и с тех пор вы ничего не освободили, это не значит, что он бросит это время. Поэтому я не вижу причин по этим причинам, почему два вызова не могут быть устранены - нет ситуации, когда стандарт гарантирует, что new char[10]
будет бросать, поэтому нет необходимости в реализации, чтобы выяснить, будет ли это или нет, Насколько вам известно, какой-то другой процесс в системе освободил 10 байт непосредственно перед вызовом new[]
и выделил его сразу после вызова delete[]
.
[*]
Или, может быть, нет. Если new
не проверяет пробел, возможно, полагаясь на защитные страницы, а просто увеличивает указатель, а delete
обычно ничего не делает (полагаясь на выход процесса в свободную память), но в частном случае, когда освобожден блок последний блок, выделенный им, уменьшает указатель, ваш код может быть эквивалентен:
// new[]
global_last_allocation = global_next_allocation;
global_next_allocation += 10 + sizeof(size_t);
char *tmp = global_last_allocation;
*((size_t *)tmp) = 10; // code to handle alignment requirements is omitted
tmp += sizeof(size_t);
// delete[]
tmp -= sizeof(size_t);
if (tmp == global_last_allocation) {
global_next_allocation -= 10 + *((size_t*)tmp);
}
Что можно было бы почти удалить, если ничего не изменено, оставив global_last_allocation = global_next_allocation;
. Вы также можете избавиться от этого, сохранив предыдущее значение last
в заголовке блока вместе с размером и восстановив это предыдущее значение при освобождении последнего выделения. Тем не менее, для реализации довольно реалистичной реализации распределителя памяти вам понадобится однопоточная программа с программистом скорости, который уверен, что программа не отбирает больше памяти, чем была доступна для начала.