Как спаривание нового [] с удалением может привести только к утечке памяти?
Прежде всего, использование delete
для чего-либо, выделенного с помощью new[]
, - это поведение undefined в соответствии со стандартом С++.
В Visual С++ 7 такое спаривание может привести к одному из двух последствий.
Если тип new [] 'ed имеет тривиальный конструктор и деструктор, VС++ просто использует new
вместо new[]
и использует delete
для этого блока отлично работает - new
просто вызывает "выделить память", delete
просто вызывает "свободную память".
Если тип new [] 'ed имеет нетривиальный конструктор или деструктор, вышеуказанный трюк не может быть выполнен - VС++ 7 должен вызывать точное количество деструкторов. Поэтому он добавляет массив с size_t
, хранящим количество элементов. Теперь адрес, возвращаемый new[]
, указывает на первый элемент, а не на начало блока. Поэтому, если используется delete
, он вызывает только деструктор для первого элемента и вызывает "свободную память" с адресом, отличным от адреса, возвращаемого "распределением памяти", и это приводит к некоторому индикатору ошибки внутри HeapFree(), который я подозреваю относится к повреждению кучи.
Однако каждый здесь и там можно читать ложные утверждения, что использование delete
после new[]
приводит к утечке памяти. Я подозреваю, что любой размер кучевого повреждения гораздо важнее факта, что деструктор вызывается только для первого элемента и, возможно, деструкторы, не вызванные, не освобождают выделенные кучи под-объекты.
Каким образом использование delete
после new[]
возможно приведет только к утечке памяти в некоторых реализациях С++?
Ответы
Ответ 1
Предположим, что я компилятор С++, и я реализую управление памятью следующим образом: я добавляю каждый блок зарезервированной памяти с размером памяти в байтах. Что-то вроде этого;
| size | data ... |
^
pointer returned by new and new[]
Обратите внимание, что с точки зрения распределения памяти нет разницы между new
и new[]
: оба просто выделяют блок памяти определенного размера.
Теперь, как delete[]
узнать размер массива, чтобы вызвать нужное количество деструкторов? Просто разделите size
блока памяти на sizeof(T)
, где T
- это тип элементов массива.
Теперь предположим, что я реализую delete
как просто один вызов деструктора, за которым следует освобождение байтов size
, тогда деструкторы последующих элементов никогда не будут вызываться. Это приводит к утечке ресурсов, выделяемых последующими элементами. Тем не менее, поскольку я освобождаю size
байты (не sizeof(T)
байты), не происходит кучное повреждение.
Ответ 2
Сказка о смешивании new[]
и delete
, якобы вызывающая утечку памяти, такова: сказка. В действительности это абсолютно не имеет никакого отношения. Я не знаю, откуда она взялась, к тому времени она приобрела собственную жизнь и выживает, как вирус, распространяясь из уст в уста от одного новичка к другому.
Наиболее вероятным объяснением этой "бессмыслицы утечки памяти" является то, что из невинно наивной точки зрения разница между delete
и delete[]
заключается в том, что delete
используется для уничтожения только одного объекта, тогда как delete[]
уничтожает массив объектов ( "много" объектов). Наивный вывод, который обычно вытекает из этого, состоит в том, что первый элемент массива будет уничтожен delete
, в то время как остальное будет сохраняться, создавая, таким образом, предполагаемую "утечку памяти". Конечно, любой программист, по крайней мере, с базовым пониманием типичных реализаций кучи, сразу поймет, что наиболее вероятным следствием этого является повреждение кучи, а не "утечка памяти".
Другим популярным объяснением наивной теории "утечки памяти" является то, что, поскольку вызываемое неправильное количество деструкторов вызвано, вторичная память, принадлежащая объектам в массиве, не освобождается. Это может быть правдой, но это, очевидно, очень принудительное объяснение, которое не имеет большого значения перед лицом гораздо более серьезной проблемы с коррупцией кучи.
Короче говоря, смешивание различных функций распределения является одной из тех ошибок, которые приводят к прочному, непредсказуемому и очень практичному поведению undefined. Любые попытки наложить некоторые конкретные ограничения на проявления этого поведения undefined - это просто трата времени и верный признак отсутствия базового понимания.
Не нужно добавлять, new/delete
и new[]/delete[]
фактически являются двумя независимыми механизмами управления памятью, которые независимо настраиваются. Как только они настраиваются (заменяя необработанные функции управления памятью), абсолютно невозможно даже предсказать, что может произойти, если они будут смешанными.
Ответ 3
Кажется, что ваш вопрос действительно "почему куча коррупции не происходит?". Ответ на этот вопрос - "потому что менеджер кучи отслеживает выделенные размеры блоков". Вернемся к C на минуту: если вы хотите выделить один int в C, вы бы сделали int* p = malloc(sizeof(int))
, если вы хотите выделить массив размера n
, вы можете либо написать int* p = malloc(n*sizeof(int))
, либо int* p = calloc(n, sizeof(int))
. Но в любом случае вы освободите его на free(p)
, независимо от того, как вы его выделили. Вы никогда не передаете размер бесплатному(), free() просто "знает", насколько свободны, потому что размер блока malloc() - ed сохраняется где-то "впереди" блока. Вернемся к С++, new/delete и new []/delete [] обычно реализуются с точки зрения malloc (хотя они не обязательно должны быть, вы не должны полагаться на это). Вот почему новая комбинация []/delete не повреждает кучу - удаление освобождает нужное количество памяти, но, как объяснялось всеми передо мной, вы можете получить утечки, не называя правильное количество деструкторов.
Тем не менее, рассуждение о поведении undefined в С++ всегда бессмысленно. Почему имеет значение, если новая комбинация []/delete работает, "только" протекает или вызывает повреждение кучи? Вы не должны так писать, период! И на практике я бы избегал ручного управления памятью, когда это было возможно, - STL и boost присутствуют по какой-то причине.
Ответ 4
Если нетривиальный деструктор, который не вызывается для всех, кроме первого элемента в массиве, должен освобождать некоторую память, вы получаете утечку памяти, так как эти объекты не очищаются должным образом.
Ответ 5
Помимо возникновения поведения undefined, самая простая причина утечек заключается в реализации, не вызывающей деструктор для всех, кроме первого объекта в массиве. Это, очевидно, приведет к утечкам, если объекты выделили ресурсы.
Это самый простой возможный класс, который я мог бы представить в результате такого поведения:
struct A {
char* ch;
A(): ch( new char ){}
~A(){ delete ch; }
};
A* as = new A[10]; // ten times the A::ch pointer is allocated
delete as; // only one of the A::ch pointers is freed.
PS: обратите внимание, что конструкторы не могут быть вызваны во многих других ошибках программирования тоже: не виртуальные деструкторы базового класса, ложная зависимость от умных указателей,...
Ответ 6
Это приведет к утечке во всех реализациях С++ в любом случае, когда деструктор освобождает память, потому что деструктор никогда не вызван.
В некоторых случаях это может привести к значительно худшим ошибкам.
Ответ 7
утечка памяти может произойти, если оператор new() переопределен, но новый [] - нет. то же самое относится к оператору delete/delete []
Ответ 8
Позднее для ответа, но...
Если ваш механизм удаления просто вызывает деструктор и помещает освобожденный указатель вместе с размером, подразумеваемым sizeof, в свободный стек, тогда вызов delete на куске памяти, выделенной новым [], приведет к потере памяти - но не коррупция.
Более сложные структуры malloc могут испортить или обнаружить такое поведение.
Ответ 9
Почему не может быть ответ, что он вызывает оба?
Очевидно, что происходит утечка памяти, происходит ли повреждение кучи или нет.
Или, вернее, так как я могу повторно внедрять новые и удалять..... не может ли это вообще ничего не вызывать. Технически я могу заставить new и delete выполнять новые [] и удалять [].
HENCE: Undefined Поведение.
Ответ 10
Я отвечал на вопрос, который был отмечен как дубликат, поэтому я просто скопирую его здесь, если он будет изменен. Было сказано, что передо мной происходит распределение памяти, я просто объясню причину и последствия.
Просто что-то прямо с google: http://en.cppreference.com/w/cpp/memory/new/operator_delete
Во всяком случае delete - это функция для одного объекта. Он освобождает экземпляр от указателя и уходит;
delete [] - это функция, используемая для освобождения массивов. Это означает, что он просто не освобождает указатель; Он объявляет весь блок памяти этого массива как мусор.
Это все на практике, но вы говорите, что ваше приложение работает. Вы, наверное, задаетесь вопросом... почему?
Решение С++ не исправляет утечки памяти. Если вы будете использовать delete без скобок, он удалит только массив как объект - процесс, который может вызвать утечку памяти.
классная история, утечка памяти, почему мне это нужно?
Утечка памяти происходит, когда выделенная память не удаляется. Тогда для этой памяти требуется ненужное дисковое пространство, из-за чего вы потеряете полезную память практически без причины. Это плохое программирование, и вы, вероятно, должны его исправить в своих системах.