Это до реализации.Выделение и освобождение памяти не является «наблюдаемым поведением», если реализация не решит, что это наблюдаемое поведение.
На практике ваша реализация, вероятно, связывается с некоторой библиотекой времени выполнения C ++, и когда ваш TU компилируется,Компилятор вынужден признать, что вызовы в эту библиотеку могут иметь наблюдаемые эффекты.Насколько я знаю, это не предписано стандартом, просто так все нормально работает.Если оптимизатор может каким-то образом определить, что определенные вызовы или комбинации вызовов на самом деле не влияют на наблюдаемое поведение, он может их удалить, поэтому я считаю, что особый случай обнаружения вашего примера кода и его удаления будет соответствовать.
Кроме того, я не могу вспомнить, как определенные пользователем глобальные new[]
и delete[]
работают [Мне напомнили].Поскольку код может вызывать определения этих вещей в другом определяемом пользователем TU, который позднее будет связан с этим TU, вызовы не могут быть оптимизированы во время компиляции.Они могут быть удалены во время соединения, если выяснится, что операторы не определены пользователем (хотя тогда применимы все, что касается библиотеки времени выполнения), или определены пользователем, но не имеют побочных эффектов (если пара из нихвстроен - это кажется довольно неправдоподобным в разумной реализации, на самом деле [*]).
Я почти уверен, что вам не разрешено полагаться на исключение из 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
в заголовке блока вместе с размером и восстановив это предыдущее значение после освобождения последнего выделения.Это довольно экстремальная реализация распределителя памяти, однако вам понадобится однопоточная программа с быстрым программистом, который уверен, что программа не использует больше памяти, чем было доступно для начала.