Почему для массивов существуют специальные функции создания и удаления?
-
20-08-2019 - |
Вопрос
Что плохого в использовании delete
вместо delete[]
?
Происходит ли что-то особенное в процессе выделения и освобождения массивов?
Почему это будет отличаться от malloc
и бесплатно?
Решение
Объекты, созданные с помощью new[]
должен использовать delete[]
.С использованием delete
не определено в массивах.
С malloc и free ситуация проще.Существует только одна функция, которая освобождает выделяемые вами данные, также не существует концепции вызова деструктора.Путаница возникает только потому, что delete[]
и удалить похожие.На самом деле это две совершенно разные функции.
Использование удаления не приведет к вызову правильной функции для удаления памяти.Он должен позвонить delete[](void*)
но вместо этого он вызывает delete(void*)
.По этой причине вы не можете полагаться на использование delete
для памяти, выделенной с помощью new[]
[16.13] Могу ли я оставить
[]
При удалении массива какого-то встроенного типа (char, int и т. Д.)?Нет!
Иногда программисты думают, что
[]
вdelete[] p
существует только поэтому компилятор назовет соответствующие деструкторы для всех элементов в массиве.Из-за этого рассуждения они предполагают, что множество встроенных типов, таких какchar
илиint
возможноdelete
г без[]
.Например, они предполагают, что следующее является действительным кодом:void userCode(int n) { char* p = new char[n]; ... delete p; // ← ERROR! Should be delete[] p ! }
Но приведенный выше код неверен, и он может вызвать катастрофу во время выполнения.В частности, код, который требуется
delete p
являетсяoperator delete(void*)
, но код, который требуетсяdelete[] p
являетсяoperator delete[](void*)
.Поведение по умолчанию для последнего состоит в том, чтобы вызвать первое, но пользователям разрешено заменить последнее другим поведением (в этом случае они обычно также заменяют соответствующий новый код в оператореnew[](size_t)
).Если они заменилиdelete[]
Код, так что он не был совместимы с кодом Delete, и вы позвонили в неправильный (т.е., если вы сказалиdelete p
скорее, чемdelete[] p
), вы можете получить катастрофу во время выполнения.
Почему delete[]
существуют изначально?
Делаете ли вы x или y:
char * x = new char[100];
char * y = new char;
Оба хранятся в char *
типизированные переменные.
Я думаю, что причина решения delete
, и delete[]
сопровождается длинным списком решений, которые способствуют повышению эффективности C++.Это сделано для того, чтобы не существовало принудительной цены на поиск того, сколько данных необходимо удалить для обычной операции удаления.
Имея 2 new
и new[]
кажется логичным иметь delete
и delete[]
во всяком случае для симметрии.
Другие советы
Разница в том, что delete
удалит только весь диапазон памяти, но вызовет деструктор только для 1 объекта. delete[]
удалит память и вызовет деструктор для каждого объекта. Если вы не используете new[]
для массивов, это только вопрос времени, когда вы внесете утечку ресурсов в свое приложение.
РЕДАКТИРОВАТЬ Обновить
В соответствии со стандартом передача объекта, выделенного с помощью <=> на <=>, не определена. Поведение вероятного заключается в том, что оно будет действовать так, как я описал. Р>
Страуструп рассказывает о причинах разделения new
/new[]
и delete/
Операторы delete[]` в разделе «Проектирование и эволюция C++» в разделах с 10.3 по 10.5.1:
- 10.3 Распределение массива - обсуждается, что им нужен способ, позволяющий выделять массивы объектов с использованием схемы, отдельной от выделения отдельных объектов (т. е. выделения массивов из отдельного хранилища).Добавление версий массива
new
иdelete
было решение для этого; - 10.5.1 Освобождение массивов - обсуждается проблема с освобождением массивов с использованием только одного
delete
заключается в том, что необходимо больше информации, чем просто указатель, чтобы определить, указывает ли указатель на первый элемент массива или он просто указывает на один объект.Вместо «усложнения обычного случая выделения и освобождения отдельных объектов»,delete[]
Оператор используется для обработки массивов.Это соответствует общей философии проектирования C++: «не платите за то, чем не пользуетесь».
Было ли это решение ошибкой или нет, вопрос спорный – у того и другого есть веские аргументы, но мы имеем то, что имеем.
Причина этого требования историческая и потому что new type
и new type [size]
возвращают разные вещи, которые нужно очистить по-разному.
Рассмотрим этот код
Foo* oneEntry = new Foo;
Foo* tenEntries = new Foo[10];
Они оба возвращают указатель Foo *, разница в том, что второй вызов приведет к тому, что конструктор Foo будет вызываться в 10 раз, а объем памяти примерно в 10 раз больше.
Итак, теперь вы хотите освободить свои объекты.
Для одного объекта вы бы назвали delete - например, delete oneEntry
. Это вызывает деструктор объектов и освобождает память.
Но вот в чем проблема - oneEntry и tenEntries являются просто указателями Foo. Компилятор не знает, указывают ли они на один, десять или тысячу элементов.
Когда вы используете специальный синтаксис delete []
. Это сообщает компилятору &, Это массив объектов, вычисляет количество и затем уничтожает их все & Quot;.
Что действительно происходит, так это то, что для <=> компилятор тайно хранит «размер» в другом месте. Когда вы вызываете delete [], он знает, что это секретное значение существует, поэтому он может узнать, сколько объектов находится в этом блоке памяти, и уничтожить их.
Тогда вы можете задать вопрос ", почему компилятор не всегда сохраняет размер? " Р>
Это отличный вопрос, и он восходит к ранним временам C ++. Было желание, чтобы для встроенных типов (char, int, float и т. Д.) Для C ++ было бы допустимо следующее:
int* ptr = new int;
free(ptr);
int* ptr = (int*)malloc(sizeof(int) * someSize);
delete ptr;
Причиной этого было ожидание того, что люди будут предоставлять библиотеки, которые возвращали динамически распределенную память, и пользователи этих библиотек не смогут узнать, использовать ли free / delete. Р>
Это стремление к совместимости означало, что размер массива не может быть сохранен как часть самого массива и должен был храниться в другом месте. Из-за этих накладных расходов (и помните, это было еще в начале 80-х годов) было решено вести эту книгу только для массивов, а не для отдельных элементов. Таким образом, массивам нужен специальный синтаксис удаления, который ищет это значение.
Причина, по которой у malloc / free такой проблемы нет, заключается в том, что они просто работают с блоками памяти и не должны беспокоиться о вызове конструкторов / деструкторов. Р>
Что касается &, почему " в заголовке: одна из целей разработки C ++ заключалась в том, чтобы не было никаких скрытых затрат. C ++ также был разработан в то время, когда каждый байт памяти все еще имел значение гораздо больше, чем сегодня. Дизайнеры языка также любят ортогональность: если вы выделяете память с помощью new[]
(вместо new
), вы должны освободить ее с помощью delete[]
.
Я не думаю, что есть техническая причина, по которой delete
не удалось вставить "!", я - массив " пометьте в заголовке блока памяти <=> (не более <=>), чтобы посмотреть позже.
new
и delete
отличаются от malloc
и free
тем, что <=> и <=> только выделяют и освобождают память; они не называют ctors или dtors.
Когда вы используете new[]
для выделения массива, вы фактически указываете C ++ размер массива. Когда вы используете malloc
, вы вместо этого сообщаете, сколько памяти выделено. В первом случае освобождение на основе размера массива не имеет смысла. В этом случае это так. Но поскольку нет разницы между указателем для массива и для одного объекта, необходима отдельная функция.