Почему не работает обычный delete?

Рейтинг: 2Ответов: 1Опубликовано: 24.11.2014

Вот в этом примере демонстрируется, как создать массив объектов, у которых конструктор принимает параметры. Если мы, как они рекомендуют, отделяем выделение памяти от вызова конструктора, обычный способ очистки уже не сработает, приходится вызывать деструкторы в цикле. Если попытаться написать delete [] foos, всё зависнет.

Но почему?

Ответы

▲ 3Принят

Если вы выделили память при помощи new[], возвращать её нужно при помощи delete[].

Если же вы выделили область памяти при помощи обыкновенного new, а потом сконструировали объекты при помощи placement new (new( &foos[ i ] ) Foo( a, b );), и освобождать надо так же: placement delete + delete на область памяти. Любое другое освобождение памяти есть по стандарту undefined behaviour, и имеет право вести себя как угодно.

В самом деле: если объект выделен при помощи new, он есть в списке heap-объектов. delete пытается найти вокруг объекта управляющие данные heap'а (например, длину куска памяти), и воспользоваться ими. Если там на самом деле таких данных не найдётся, произойдёт катастрофа. В вашем случае именно это и происходит, только с delete[]. Кроме того, delete[] к тому же пытается вызвать delete для каждого из объектов, для чего ему нужно знать их количество, сохраняемое в управляющем блоке во время new[]. Поскольку new[] не вызывался, всё на самом деле довольно кисло.


Кстати, код, на который вы ссылаетесь, насколько я понимаю, тоже не вполне верен: он считает, что по каждый объект уходит ровно sizeof(Foo) байт памяти, то есть для count объектов нужно sizeof( Foo ) * count байт памяти. Это неверно, если компилятор решит использовать выравнивание. (Например, объект нечётной длины может спровоцировать проблемы.) Вот обсуждение по теме на братском ресурсе.