Имеем следующий код:
#define T 2 class A { public: virtual ~A() { p = 0; } int p; }; class B: public A { int a; }; int main() { A* a = new B[T]; delete[] a; return 0; }
У меня эта программа однозначно падает с Segmentation fault на строке delete[] a. Проверено на Sun C++ на Солярисе, GCC на Линуксе и на FreeBSD. Вот, например, что происходит на BSD:
Program received signal SIGSEGV, Segmentation fault.
0x08048743 in main () at new_array.cpp:17
17 delete[] a;
Забавно, что под Windows в VS2008 ничего особенного не происходит.
Как я понимаю, что в этой программе принципиально важно, чтобы она падала: деструктор класса A должен быть виртуальным, дочерний класс B должен быть больше по размеру (тут есть член a), константа Т должна быть 2 или более (то есть мы должны создавать несколько экземпляров класса B), и деструктор класса A должен что-нибудь писать в свои члены (тут есть p = 0;).
Что же тут происходит?
new[] создает массив экземплятор класса B. Оператор же delete[] получает на вход указатель типа A* и начинает вызывать деструкторы элементов. Так как деструктор класса А виртуальный, то в ход пускается таблица виртуальных функций. Итак, отработал деструктор для первого элемента a[0]. Далее delete[] хочет получить адрес следующего элемента массиве a. И для этого (внимание!) адрес следующего он вычисляется так: a + sizeof(A) (ему же на вход дали указатель типа A*). Но проблема в том, что sizeof(A) < sizeof(B) (это дает член класса B::a), и a + sizeof(A) будет указывать не на второй элемент в массиве a, а куда-то между первым и вторым элементом, так как реальный адрес второго элемента - a + sizeof(B). И все бы ничего, но деструктор класс A пишет в член p, тем самым меняя содержимое памяти, а так как для второго элемента адрес вычислен неправильно (его this указывает непонятно куда), то куда реально попадет 0 в присваивании p = 0; уже никто не знает, но явно не туда, куда надо. Вот и Segmentation fault.
Другого объяснения у меня нет.
Если кто знает лучше, поправьте.
P.S. Забавно, что под виндами ничего страшного не происходит.
Update: В комментариях дали точное объяснение из стандарта: C++ 2003 5.3.5:
…In the second alternative (delete array), the value of the operand of delete shall be the pointer value which resulted from a previous array new-expression. If not, the behavior is undefined. [Note: this means that the syntax of the delete-expression must match the type of the object allocated by new, not the syntax of the new-expression.]
Update 2: Объяснение, почему не глючит в Visual Studio.