Что не так с использованием delete
вместо delete[]
?
Есть ли что-то особенное в обложках для выделения и освобождения массивов?
Почему он отличается от malloc
и свободен?
Что не так с использованием delete
вместо delete[]
?
Есть ли что-то особенное в обложках для выделения и освобождения массивов?
Почему он отличается от malloc
и свободен?
Объекты, созданные с помощью new[]
, должны использовать delete[]
. Использование delete
undefined на массивах.
С malloc и свободным вы имеете более простую ситуацию. Есть только 1 функция, которая освобождает данные, которые вы выделяете, нет никакой концепции деструктора, вызываемого либо. Путаница приходит только потому, что delete[]
и удалить похожий вид. На самом деле это две совершенно разные функции.
Использование delete не вызовет правильную функцию для удаления памяти. Он должен вызывать delete[](void*)
, но вместо этого он вызывает delete(void*)
. По этой причине вы не можете полагаться на использование delete
для памяти, выделенной с помощью new[]
См. часто задаваемые вопросы по С++
[16.13] Можно ли сбросить
[]
, когда удаление массива некоторого встроенного типа (char, int и т.д.)?Нет!
Иногда программисты считают, что
[]
вdelete[] p
существует только так компилятор вызовет соответствующий деструкторы для всех элементов в массив. Из-за этих рассуждений они предположим, что массив некоторых встроенных тип, такой какchar
илиint
, может бытьdelete
d без[]
. Например, они предположим, что действительный код: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 p
скорее чемdelete[] p
), вы могли бы с бедой во время выполнения.
Почему delete[]
существует в первую очередь?
Выполняете ли вы x или y:
char * x = new char[100];
char * y = new char;
Оба сохраняются в char *
типизированных переменных.
Я думаю, что причина решения delete
и delete[]
согласуется с длинным списком решений, которые выступают за эффективность в С++. Это так, что нет никакой принудительной цены для поиска того, сколько нужно удалить для нормальной операции удаления.
Имея 2 new
и new[]
, кажется логичным иметь delete
и delete[]
в любом случае для симметрии.
Разница в том, что delete
удалит весь диапазон памяти, но вызовет только деструктор для 1 объекта. delete[]
удалит память и вызовет деструктор для каждого отдельного объекта. Если вы не используете delete[]
для массивов, это всего лишь вопрос времени, прежде чем вы вводите утечку ресурсов в ваше приложение.
ИЗМЕНИТЬ Обновить
В соответствии со стандартом передача объекта, выделенного с помощью new[]
to delete
, составляет undefined. Поведение Вероятно заключается в том, что он будет действовать, как я описал.
Stroustrup рассказывает о причинах отдельных new
/new[]
и delete/
удаления [] `операторов в "Проекте и эволюции С++" в разделах с 10.3 по 10.5.1:
new
и delete
было для этого решением;delete
, состоит в том, что для определения того, указана ли указатель, должна быть больше информации, чем указатель указывает на первый элемент массива или просто указывает на один объект. Вместо "усложнения общего случая выделения и освобождения отдельных объектов" оператор delete[]
используется для обработки массивов. Это вписывается в общую философию дизайна С++ "не платите за то, что вы не используете".ли это решение было ошибкой или не является дискуссионным. - в любом случае имеет хорошие аргументы, но мы имеем то, что имеем
Причина этого требования исторична, и поскольку new type
и new type [size]
возвращают разные вещи, которые нужно очищать по-другому.
Рассмотрим этот код
Foo* oneEntry = new Foo;
Foo* tenEntries = new Foo[10];
Оба возвращают указатель Foo *, разница в том, что второй вызов приведет к тому, что конструктор Foo будет вызван 10x и будет иметь примерно 10-кратное количество памяти.
Итак, теперь вы хотите освободить свои объекты.
Для одного объекта вы вызываете delete - например. delete oneEntry
. Это вызывает деструктор объектов и освобождает память.
Но здесь проблема - oneEntry и tenEntries - оба только указатели Foo. Компилятор не знает, указывают ли они на один, десять или тысячу элементов.
При использовании специального синтаксиса delete []
. Это говорит компилятору: "Это массив объектов, вычисляйте счет, а затем уничтожайте их все".
Что действительно происходит, так это то, что для new type [size]
компилятор тайно хранит "размер" где-то в другом месте. Когда вы вызываете delete [], он знает, что это секретное значение существует, чтобы он мог узнать, сколько объектов находится в этом блоке памяти и уничтожить их.
Вопрос, который вы могли бы задать, - "почему компилятор всегда сохраняет размер?"
Это отличный вопрос, и это относится к ранним дням С++. Было желание, что для встроенных типов (char, int, float и т.д.) Для С++ будет справедливо следующее:
int* ptr = new int;
free(ptr);
int* ptr = (int*)malloc(sizeof(int) * someSize);
delete ptr;
Обоснованием этого было ожидание того, что люди будут предоставлять библиотеки, которые возвращают динамически выделенную память, и пользователи этих библиотек не смогут узнать, использовать ли это бесплатное/удалить.
Это стремление к совместимости означает, что размер массива не может быть сохранен как часть самого массива и должен храниться в другом месте. Из-за этих накладных расходов (и помните, что это было в начале 80-х), было решено сделать эту книгу, сохраняя только массивы, а не отдельные элементы. Таким образом, массивы нуждаются в специальном синтаксисе удаления, который ищет это значение.
Причина, по которой malloc/free не имеет этой проблемы, заключается в том, что они просто имеют дело с блоками памяти и не должны беспокоиться о вызове конструкторов/деструкторов.
Что касается "почему" в названии: одна из целей дизайна С++ заключалась в том, что не было бы никаких скрытых затрат. С++ также разрабатывался в то время, когда каждый байт памяти все еще имел значение намного больше, чем сегодня. Дизайнеры языка также любят ортогональность: если вы выделяете память с помощью new[]
(вместо new
), вы должны освободить ее с помощью delete[]
.
Я не думаю, что существует какая-либо техническая причина, по которой new[]
не может вставить флаг "Я - массив" в заголовке блока памяти для delete
(не более delete[]
), чтобы посмотреть на позже.
new и delete отличаются от malloc и свободны в этом malloc, а бесплатно выделяют и освобождают память; они не называют ctors или dtors.
Когда вы используете новый [] для выделения массива, вы фактически указываете С++ размер массива. Когда вы используете malloc, вы говорите, сколько памяти выделено. В первом случае освобождение в зависимости от размера массива не имело бы смысла. В этом случае это так. Но поскольку нет никакой разницы между указателем на массив против одного объекта, требуется отдельная функция.