struct A
{
int a;
A() { }
virtual ~A() { }
};
struct B: public A
{
int b;
B() { }
~B() { }
};
int main (void)
{
A *ptr;
ptr = new B[2];
delete[] ptr;
}
падает на delete[]. компилятор gcc 3.4.4
заранее спасибо за помощь!
Arrays are ill-adjusted: Treating arrays polymorphically is a gross type error that your compiler will probably remain silent about. Don't fall into the trap.
Или русскими словами в моем вольном переводе: не работайте с массивами полиморфно — самим же хуже будет
> struct A
> {
> int a;
> A() { }
> virtual ~A() { }
> };
>
> struct B: public A
> {
> int b;
> B() { }
> ~B() { }
> };
>
> int main (void)
> {
> A *ptr;
> ptr = new B[2];
> delete[] ptr;
> }
>
> > падает на delete[]. компилятор gcc 3.4.4 > заранее спасибо за помощь!
питаясь визвать деструктора, он проходит поочередно по кадому обекту, додавая к базовому указателю размер нужного типа — и компилятор думает что нужный тип ето A, когда на самом деле ето B, и виходит что уже ко второму обекту доступаетса за неверним указателем, поскольку sizeof(A) != sizeof(b) — вот здесь все и летит ...
если нежен масив указателей на базовий клас то лутше сделать ево в ручную:
#include <iostream>
class A
{
public:
int a;
A(){ a=10; }
virtual ~A(){ std::cout << "A::~A" << std::endl; }
virtual void foo() = 0;
};
class B
:
public A
{
public:
int b;
B(){ b=11; }
virtual ~B(){ std::cout << "B::~B" << std::endl; }
virtual void foo(){std::cout << "B::foo" << std::endl;}
};
int main (void)
{
A *ptr[2];
B *ptr_b = new B[2];
for(int i = 0; i < 2; ++i)
{
ptr[i] = ptr_b+i;
}
ptr[0]->foo();
ptr[1]->foo();
delete[] ptr_b;
}
Здравствуйте, Bell, Вы писали:
B>А вот VC6 и VC7.1 отрабатывают эту ситуацию вполне корректно. Интересно, кто еще, кроме gcc 3.4.4 завалится на этом коде?
Не может быть, sizeof(A)!=sizeof(B), да и если бы размеры были равны, деструкторы вызывались бы невиртуально (все это, естественно, домыслы, ибо UB есть UB).
з.ы.
— А вдоль дороги — мертвые с косами стоят. И тишина...
— Брехня!
Здравствуйте, Глеб Алексеев, Вы писали:
ГА>Здравствуйте, Bell, Вы писали:
B>>А вот VC6 и VC7.1 отрабатывают эту ситуацию вполне корректно. Интересно, кто еще, кроме gcc 3.4.4 завалится на этом коде? ГА>Не может быть, sizeof(A)!=sizeof(B), да и если бы размеры были равны, деструкторы вызывались бы невиртуально (все это, естественно, домыслы, ибо UB есть UB).
Все это я знаю. И тем не менее, эти компиляторы корректно убивают массив. Видимо, все дело в том, что при создании массива с помощью new[] в блоке служебной информации записывается размер объекта B, который и используетя потом для вычисления смещения от стартового указателя.
ГА>з.ы. ГА>- А вдоль дороги — мертвые с косами стоят. И тишина... ГА>- Брехня!
Здравствуйте, Bell, Вы писали:
ГА>>Не может быть, sizeof(A)!=sizeof(B), да и если бы размеры были равны, деструкторы вызывались бы невиртуально (все это, естественно, домыслы, ибо UB есть UB).
B>Все это я знаю.
Ни разу не сомневаюсь (никакой иронии) . B>И тем не менее, эти компиляторы корректно убивают массив. Видимо, все дело в том, что при создании массива с помощью new[] в блоке служебной информации записывается размер объекта B, который и используетя потом для вычисления смещения от стартового указателя.
Ух, задело меня это дело за живое, и полез я со своими знаниями ассемблера в дизассемблер .
Оказывается, всей магией заведуют виртуальные деструкторы.
При вызове A* ptr = new B[n] выделяется sizeof(B)*n+4 байт памяти, по адресу ((char*)ptr)-4 сохраняется размер массива. И все. Прикол в том, что 0-м элементом в vftable в этом случае является указатель на `vector deleting destructor', который принимает дополнительный аргумент — размер массива. Вызов delete[] p извлекает размер массива по адресу p-4, адрес `vector deleting destructor' по адресу p и вызывает `vector deleting destructor', который, естественно, знает размер объекта.
Здравствуйте, Глеб Алексеев, Вы писали:
ГА>адрес `vector deleting destructor' по адресу p и вызывает `vector deleting destructor', который, естественно, знает размер объекта.
Вот здесь неточность, по адресу p, естественно, указатель на vftable, а vftable[0] = &B::`vector deleting destructor'.
ArtDenis,
> А>падает на delete[]. компилятор gcc 3.4.4 > А>заранее спасибо за помощь!
> Уверен почти на 100%, что VladD2 будет использовать этот топик в качестве доказательства превосходства C# над C++ в следующем споре вида "C++ vs C#"
Дык, в C# просто-напросто нет возможности создания массивов полиморфных типов по значению... В C++ есть, но с известной опасностью начать работать через указатель на базовый класс.
Posted via RSDN NNTP Server 2.0
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен