Подскажите где ошибка
От: Аноним  
Дата: 30.11.05 09:17
Оценка:
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
заранее спасибо за помощь!
Re: Подскажите где ошибка
От: Bell Россия  
Дата: 30.11.05 09:24
Оценка: 40 (5)
Здравствуйте, Аноним, Вы писали:

Как сказали товарищи Саттер и Александреску:

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.


Или русскими словами в моем вольном переводе: не работайте с массивами полиморфно — самим же хуже будет
Любите книгу — источник знаний (с) М.Горький
Re[2]: Подскажите где ошибка
От: Bell Россия  
Дата: 30.11.05 10:07
Оценка:
Здравствуйте, Bell, Вы писали:

А вот VC6 и VC7.1 отрабатывают эту ситуацию вполне корректно. Интересно, кто еще, кроме gcc 3.4.4 завалится на этом коде?
Любите книгу — источник знаний (с) М.Горький
Re: Подскажите где ошибка
От: andrij Украина  
Дата: 30.11.05 10:09
Оценка:
On Wed, 30 Nov 2005 11:17:46 +0200, wrote:

>
> 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;
}


и тогда все будет работать
Posted via RSDN NNTP Server 1.9
make it simple as possible, but not simpler
Re[3]: Подскажите где ошибка
От: Глеб Алексеев  
Дата: 30.11.05 10:16
Оценка: -1
Здравствуйте, Bell, Вы писали:

B>А вот VC6 и VC7.1 отрабатывают эту ситуацию вполне корректно. Интересно, кто еще, кроме gcc 3.4.4 завалится на этом коде?

Не может быть, sizeof(A)!=sizeof(B), да и если бы размеры были равны, деструкторы вызывались бы невиртуально (все это, естественно, домыслы, ибо UB есть UB).

з.ы.
— А вдоль дороги — мертвые с косами стоят. И тишина...
— Брехня!
... << RSDN@Home 1.2.0 alpha rev. 619>>
Re[4]: Подскажите где ошибка
От: Bell Россия  
Дата: 30.11.05 10:27
Оценка: 6 (1)
Здравствуйте, Глеб Алексеев, Вы писали:

ГА>Здравствуйте, Bell, Вы писали:


B>>А вот VC6 и VC7.1 отрабатывают эту ситуацию вполне корректно. Интересно, кто еще, кроме gcc 3.4.4 завалится на этом коде?

ГА>Не может быть, sizeof(A)!=sizeof(B), да и если бы размеры были равны, деструкторы вызывались бы невиртуально (все это, естественно, домыслы, ибо UB есть UB).

Все это я знаю. И тем не менее, эти компиляторы корректно убивают массив. Видимо, все дело в том, что при создании массива с помощью new[] в блоке служебной информации записывается размер объекта B, который и используетя потом для вычисления смещения от стартового указателя.

ГА>з.ы.

ГА>- А вдоль дороги — мертвые с косами стоят. И тишина...
ГА>- Брехня!

Любите книгу — источник знаний (с) М.Горький
Re[5]: Подскажите где ошибка
От: Глеб Алексеев  
Дата: 30.11.05 11:44
Оценка: 23 (4)
Здравствуйте, 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', который, естественно, знает размер объекта.
... << RSDN@Home 1.2.0 alpha rev. 619>>
Re[6]: Подскажите где ошибка
От: Глеб Алексеев  
Дата: 30.11.05 11:55
Оценка:
Здравствуйте, Глеб Алексеев, Вы писали:

ГА>адрес `vector deleting destructor' по адресу p и вызывает `vector deleting destructor', который, естественно, знает размер объекта.

Вот здесь неточность, по адресу p, естественно, указатель на vftable, а vftable[0] = &B::`vector deleting destructor'.
... << RSDN@Home 1.2.0 alpha rev. 619>>
Re: Подскажите где ошибка
От: ArtDenis Россия  
Дата: 30.11.05 17:55
Оценка: :))) :))
А>падает на delete[]. компилятор gcc 3.4.4
А>заранее спасибо за помощь!

Уверен почти на 100%, что VladD2 будет использовать этот топик в качестве доказательства превосходства C# над C++ в следующем споре вида "C++ vs C#"
... << RSDN@Home 1.1.4 stable rev. 510>>
[ 🎯 Дартс-лига Уфы | 🌙 Программа для сложения астрофото ]
Re[2]: Подскажите где ошибка
От: Павел Кузнецов  
Дата: 02.12.05 03:23
Оценка:
ArtDenis,

> А>падает на delete[]. компилятор gcc 3.4.4

> А>заранее спасибо за помощь!

> Уверен почти на 100%, что VladD2 будет использовать этот топик в качестве доказательства превосходства C# над C++ в следующем споре вида "C++ vs C#"


Дык, в C# просто-напросто нет возможности создания массивов полиморфных типов по значению... В C++ есть, но с известной опасностью начать работать через указатель на базовый класс.
Posted via RSDN NNTP Server 2.0
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.