Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > Редкая, но возможная грабля


Автор: FunnyFalcon 13.12.2006, 15:55
Код

#include <cstdio>

class A {
    int i;
};

class B: public A {
    int j;
    virtual void d(){}
};

int main(){
    B* b = new B();
    A* a = b;
    printf("%p %p\n", a, b);
    delete a;
    return 0;
};

Код

yura@falcon:~/Temp/C$ c++ inherit.cpp
yura@falcon:~/Temp/C$ a.out
0x804a00c 0x804a008
*** glibc detected *** free(): invalid pointer: 0x0804a00c ***
Aborted

g++ 3.4.6, 4.0.x

Это известная фича? А как в других компиляторах?

Автор: Sartorius 13.12.2006, 16:02
 А ты попробуй dynamic_cast-ом B в delete преобразовать... Останется еггог?

Автор: FunnyFalcon 13.12.2006, 16:59
dinamic_cast может и сработает. 
Но допустим, мы не знаем, на объект какого класса a на самом деле ссылается? (А иначе зачем нам наследование?)

Автор: Fazil6 13.12.2006, 18:25
а в чем собственно фича?
по моему все правильно, как и должно было быть

Добавлено @ 18:27 
добавь в A
Код

public:
    virtual ~A(){};

и ексепшена не будет

Автор: Daevaorn 13.12.2006, 18:58
Цитата(FunnyFalcon @  13.12.2006,  16:55 Найти цитируемый пост)
Это известная фича? 

Это известная ошибка начинающих разработчиков на C++

Автор: Sartorius 13.12.2006, 19:05
Daevaorn, точна smile  деструктора то виртуального нет))

Автор: FunnyFalcon 14.12.2006, 14:02
Проблема в том, что вообще ничего виртуального нет в классе A.
И поэтому не выделяется указатель на виртуальную таблицу.
А когда наследуем и создаём виртуальный метод, то указатель на вирт.таблицу добавляется в начало.
Для чего я и распечатывал значения указателей - они разные, 
и именно поэтому и выскакивает ошибка - я пытаюсь удалить по неправильной ссылке.

Первый вопрос у меня возник по-этому поводу - ну почему указатель выртуальной таблицы добавляется в начало? 
добавься он после A.i, ошибки не было бы.
Вот я и спрашиваю - все компиляторы в данной ситуации ведут себя так же?

Идея была использовать не чистый С++, реализовать альтернативу виртуальной таблице. 
Но найдется программер, который отнаследует и приляпает виртуальный метод. И что? Тут же всё пойдёт насмарку?
Потому-что компилятор, видите ли, любит, чтобы таблица всегда была сначала?

Се ля ви (такова жизнь).

Это не наезд. Я действительно новичок в С++. Просто есть вещи, не до конца подчиняющиеся (моей) логике.

Автор: Romikgy 14.12.2006, 15:01
имхо некоректный код
воть немного подправил для наглядности
Код

#include <cstdlib>
#include <iostream>

using namespace std;
class A {
    int i;
    public:
    A(){cout<<"Constr A()\n";}
    ~A(){cout<<"Destr A()\n";}
};
class B: public A {
    int j;
    virtual void d(){}
    public:
    B(){cout<<"Constr B()\n";}
    virtual
    ~B(){cout<<"Destr B()\n";}
};
int main(int argc, char *argv[])
{
    //////////////////////////////////////
    B* b = new B();
    A* a = b;
    printf("%p %p\n", a, b);
    delete a;
    /////////////////////////////////////////
    system("PAUSE");
    return EXIT_SUCCESS;
}

посмотри что выдает!

Автор: FunnyFalcon 14.12.2006, 18:53
Код

yura@falcon:~/Temp/C$ g++ -o inherit1 inherit1.cpp
yura@falcon:~/Temp/C$ inherit1
Constr A()
Constr B()
0x804b00c 0x804b008
Destr A()
*** glibc detected *** free(): invalid pointer: 0x0804b00c ***
Aborted


gcc 3.4.6. А у тебя какой компилятор?

Автор: Romikgy 14.12.2006, 19:06
gcc 3.4.2 (только под винду)
Код

Constr A()
Constr B()
003D3FD4 003D3FD0
Destr A()


FunnyFalcon, так ты видишь в чем ошибка?

Автор: Fazil6 14.12.2006, 21:57
Цитата

FunnyFalcon, так ты видишь в чем ошибка?

и в чем же? о какой ошибке идет речь?

Автор: FunnyFalcon 15.12.2006, 09:32
Код

*** glibc detected *** free(): invalid pointer: 0x0804b00c ***
Aborted

По-моему это довольно весомая ошибка. Или я не прав?

Программа скомпиленная вашим компилятором этой ошибки не выдаёт. Но вот это мне подсказывает, что в большой программе могут возникнуть большие трудности:
Цитата

Код

003D3FD4 003D3FD0


УКАЗАТЕЛИ РАЗНЫЕ!!!! И что тогда удалает delete? Какую память освобождает?

Автор: Fazil6 15.12.2006, 10:47
Цитата(FunnyFalcon @  15.12.2006,  09:32 Найти цитируемый пост)
По-моему это довольно весомая ошибка. Или я не прав?

Программа скомпиленная вашим компилятором этой ошибки не выдаёт. Но вот это мне подсказывает, что в большой программе могут возникнуть большие трудности

любой компилер здесь должен вызывать ошибку.
Я не врубаюсь на что намекает Romikgy и что он хочет своим примером показать. 
Цитата(FunnyFalcon @  15.12.2006,  09:32 Найти цитируемый пост)
УКАЗАТЕЛИ РАЗНЫЕ!!!! И что тогда удалает delete? Какую память освобождает?

значения указателей разные потому, что здесь компиллятор приводит из типа B* в А* и при удалении  а он естественно вызывеет его деструктор так как а имеет тип А*, а фактически объект имеет тип В и должен быть вызван его деструктор и чтобы в такой ситуации вызывался правильный деструктор все деструкторы должны быть виртуальными, чтобы выбирался деструктор не по типу, а по фактическому объекту.

Автор: Romikgy 15.12.2006, 12:43
Цитата(Fazil6 @  15.12.2006,  09:47 Найти цитируемый пост)
Я не врубаюсь на что намекает Romikgy


Цитата(Fazil6 @  15.12.2006,  09:47 Найти цитируемый пост)
значения указателей разные потому, что здесь компиллятор приводит из типа B* в А* и при удалении  а он естественно вызывеет его деструктор так как а имеет тип А*, а фактически объект имеет тип В и должен быть вызван его деструктор и чтобы в такой ситуации вызывался правильный деструктор все деструкторы должны быть виртуальными, чтобы выбирался деструктор не по типу, а по фактическому объекту.

вот на это и намекал! (неправильная работа с указателями)
Цитата(FunnyFalcon @  15.12.2006,  08:32 Найти цитируемый пост)
003D3FD4 003D3FD0

на это как то не обратил внимание smile
хотя вроде объяснили

PS правда не понятно , ведь это неявное преобразование указателей!!!
в C++ Builder 6 такого преобразования нет , указатели одни и теже!
Код

Constr A()
Constr B()
008F5BD8 008F5BD8
Destr A()

Автор: FunnyFalcon 15.12.2006, 13:14
Цитата(Romikgy @  15.12.2006,  12:43 Найти цитируемый пост)
PS правда не понятно , ведь это неявное преобразование указателей!!!
в C++ Builder 6 такого преобразования нет , указатели одни и теже!
Код

Constr A()
Constr B()
008F5BD8 008F5BD8
Destr A()


Значит C++Builder 6 в данном вопросе - наиболее логичный компилятор.
А как будет выглядеть результат работы следующего кода:
Код

#include <cstdlib>
#include <iostream>
using namespace std;
class A {
    public:
    int i;
    A(){cout<<"Constr A()\n";}
    ~A(){cout<<"Destr A()\n";}
};
class B: public A {
    public:
    int j;
    B(){cout<<"Constr B()\n";}
    virtual
    ~B(){cout<<"Destr B()\n";}
};
int main(int argc, char *argv[])
{
    //////////////////////////////////////
    B* b = new B();
    b->i = 1;
    b->j = 2;
    int* ar = reinterpret_cast<int*>b;
    printf("%i %i %i\n", ar[0],ar[1],ar[2]);
    delete b;
    /////////////////////////////////////////
    return EXIT_SUCCESS;
}


На gcc-3.4.6 вышло:
Код

Constr A()
Constr B()
134516040 1 2
Destr B()
Destr A()

Откуда видим, что указатель на в. таблицу приляпался к началу.
Видимо на С++Builder-е будет что-то вроде
Код

Constr A()
Constr B()
1 134516040 2
Destr B()
Destr A()

Пришлёшь результат?

Автор: Romikgy 15.12.2006, 14:29
Код

Constr A()
Constr B()
1 4203200 2
Destr B()
Destr A()


Автор: FunnyFalcon 15.12.2006, 14:53
Ну что ж. Borland есть за что похвалить. А как у MS C++ дела обстоят?

Автор: _stranger_ 15.12.2006, 15:15
Visiual Studio 7.0

Код

Constr A()
Constr B()
4780244 1 2
Destr B()
Destr A()


Автор: FunnyFalcon 15.12.2006, 15:55
Цитата(_stranger_ @  15.12.2006,  15:15 Найти цитируемый пост)

Visiual Studio 7.0
Код

Constr A()
Constr B()
4780244 1 2
Destr B()
Destr A()


Жаль. Самый верный способ отнаследовать от одного родителя, и дался в зубы только Borland-у.
Очень жаль.

Автор: Fazil6 15.12.2006, 15:58
Цитата

Жаль. Самый верный способ отнаследовать от одного родителя, и дался в зубы только Borland-у.
Очень жаль.

хоть там и нет эксепшена, но работает этот код по твоему алгоритму неправильно, так что еще неизвестно, что лучше, когда эксепшен вылетает или когда втихую пашет, но 100% неправильно.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)