Вот в этом примере демонстрируется, как создать массив объектов, у которых конструктор принимает параметры. Если мы, как они рекомендуют, отделяем выделение памяти от вызова конструктора, обычный способ очистки уже не сработает, приходится вызывать деструкторы в цикле. Если попытаться написать delete [] foos, всё зависнет.
Но почему?
Если вы выделили память при помощи 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 байт памяти. Это неверно, если компилятор решит использовать выравнивание. (Например, объект нечётной длины может спровоцировать проблемы.) Вот обсуждение по теме на братском ресурсе.
Сборка персонального компьютера от Artline: умный выбор для современных пользователей