Вопрос про полиморфизм
От: blacksun  
Дата: 26.06.11 20:10
Оценка:
using namespace std;

class A
{
public:

    A()
    {
        func2();
    }

    void func1()
    {
        func2();
    }

    virtual void func2()
    {
        cout << "A func2" << endl;
    }
};


class B : public A
{
public:
    void func2()
    {
        cout << "B func2" << endl;
    }
};


int _tmain(int argc, _TCHAR* argv[])
{

    B b;
    b.func1();

    A *aa = dynamic_cast<A *>(&b);
    aa->func1();

    return 0;
}



При запуске программы, имеем следующий вывод:

A func2
B func2
B func2


Можно прокомментировать всю подноготную вопроса??
Я понимаю следующим образом:
С первой строкой все ясно, вызвалась из конструктора A, B еще и помине нет, поэтому "B func2" мы по любому бы никогда не увидели
Со второй тоже вроде как ясно. Вызываем унаследованный func1, он пытается вызвать func2, происходит поиск по таблице виртуальных функций обьекта B, находится перегруженный func2 и вызывается именно он.
А во с третьей строкой непонятно. Почему так??
Re: Вопрос про полиморфизм
От: glap  
Дата: 26.06.11 20:17
Оценка:
Так в 3-м ничего не изменилось. Всё как и во втором. Ищет в таблице и находит.
Re[2]: Вопрос про полиморфизм
От: blacksun  
Дата: 26.06.11 20:30
Оценка:
Здравствуйте, glap, Вы писали:

G>Так в 3-м ничего не изменилось. Всё как и во втором. Ищет в таблице и находит.


Но в третьем случае мы типа как с объектом класса А работаем. Т.е. по идее надо бы ограничить эти поиски классом А и базовыми для А, если они есть. Я просто не понимаю до конца.
Re: Вопрос про полиморфизм
От: 24  
Дата: 26.06.11 20:37
Оценка: 1 (1)
Здравствуйте, blacksun, Вы писали:

B>А во с третьей строкой непонятно. Почему так??


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

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

Рассмотрим пример:
class Animal {
    virtual void voice() = 0;
};

class Cat : public Animal {
    virtual void voice() {std::cout << "Meow" << std::endl;}
};

class Dog : public Animal {
    virtual void voice() {std::cout << "Arf" << std::endl;}
};

Animal* createAnimal() {
    if (...) {
        return new Cat();
    } else {
        return new Dog();
    }
}


В использующем это коде неизвестен точный тип, но функция вызовется нужная:
Animal* animal = createAnimal();
animal->voice();


Иначе (если всегда нужно знать точный тип), чем виртуальная функция отличалась бы от невиртуальной?
Re[3]: Вопрос про полиморфизм
От: glap  
Дата: 26.06.11 20:37
Оценка:
Здравствуйте, blacksun, Вы писали:

B>Здравствуйте, glap, Вы писали:


G>>Так в 3-м ничего не изменилось. Всё как и во втором. Ищет в таблице и находит.


B>Но в третьем случае мы типа как с объектом класса А работаем. Т.е. по идее надо бы ограничить эти поиски классом А и базовыми для А, если они есть. Я просто не понимаю до конца.


Таблица виртуальных методов всегда одна и оттого, что ты сделал приведение к базовому классу с ней ничего не случится.
В этом ведь вся соль полиморфизма, чтоб ты мог обращаться к методам реально созданного класса независимо от типа указателя.
Re[4]: Вопрос про полиморфизм
От: blacksun  
Дата: 26.06.11 21:15
Оценка:
Здравствуйте, glap, Вы писали:


G>Таблица виртуальных методов всегда одна и оттого, что ты сделал приведение к базовому классу с ней ничего не случится.

G>В этом ведь вся соль полиморфизма, чтоб ты мог обращаться к методам реально созданного класса независимо от типа указателя.

спасибо, теперь ясно.
Re[4]: Вопрос про полиморфизм
От: GhostCoders Россия  
Дата: 27.06.11 12:41
Оценка:
еще надо не забывать про срезку "объекта"

например, изучите и сравните два примера


пример1 (продолжение твоего)

int _tmain(int argc, _TCHAR* argv[])
{

    B b;
    b.func1();

    A *aa = dynamic_cast<A *>(&b);
    aa->func1();

    try{
        throw b;
    }catch(A a)
    {
        a.func1();
    }

    return 0;
}


пример два

int _tmain(int argc, _TCHAR* argv[])
{

    B b;
    b.func1();

    A *aa = dynamic_cast<A *>(&b);
    aa->func1();

    try{
        throw b;
    }catch(A & a)
    {
        a.func1();
    }

    return 0;
}


вывод первого
A func2
B func2
B func2
A func2

вывод второго
A func2
B func2
B func2
B func2


Здравствуйте, glap, Вы писали:

G>Здравствуйте, blacksun, Вы писали:


B>>Здравствуйте, glap, Вы писали:


G>>>Так в 3-м ничего не изменилось. Всё как и во втором. Ищет в таблице и находит.


B>>Но в третьем случае мы типа как с объектом класса А работаем. Т.е. по идее надо бы ограничить эти поиски классом А и базовыми для А, если они есть. Я просто не понимаю до конца.


G>Таблица виртуальных методов всегда одна и оттого, что ты сделал приведение к базовому классу с ней ничего не случится.

G>В этом ведь вся соль полиморфизма, чтоб ты мог обращаться к методам реально созданного класса независимо от типа указателя.
Третий Рим должен пасть!
Re[5]: Вопрос про полиморфизм
От: blacksun  
Дата: 28.06.11 20:04
Оценка:
Здравствуйте, GhostCoders, Вы писали:

GC>еще надо не забывать про срезку "объекта"


GC>например, изучите и сравните два примера



GC>пример1 (продолжение твоего)


GC>
GC>    try{
GC>        throw b;
GC>    }catch(A a)
GC>    {
GC>        a.func1();
GC>    }
GC>}
GC>


GC>пример два


GC>
GC>    try{
GC>        throw b;
GC>    }catch(A & a)
GC>    {
GC>        a.func1();
GC>    }
GC>}
GC>


GC>вывод первого

GC>A func2
GC>B func2
GC>B func2
GC>A func2

GC>вывод второго

GC>A func2
GC>B func2
GC>B func2
GC>B func2

А можно пояснить подробней?? Я так понимаю, в первом случае в обработчик кетч передается обьект по значению, т.е. его копия, соответственно вызывается конструктор копирования A. И на этом этапе таблицы виртуальных функций как-то теряются)))
Во втором случае передается по ссылке, по сути по указателю, соответственно объект тот же и все полиморфные фишки работают.
Re[6]: Вопрос про полиморфизм
От: GhostCoders Россия  
Дата: 30.06.11 09:36
Оценка:
Здравствуйте, blacksun, Вы писали:

B>А можно пояснить подробней?? Я так понимаю, в первом случае в обработчик кетч передается обьект по значению, т.е. его копия, соответственно вызывается конструктор копирования A. И на этом этапе таблицы виртуальных функций как-то теряются)))

B>Во втором случае передается по ссылке, по сути по указателю, соответственно объект тот же и все полиморфные фишки работают.

Ключевое слово здесь срезка.
Третий Рим должен пасть!
Re: Вопрос про полиморфизм
От: chijo Россия  
Дата: 01.07.11 07:00
Оценка:
Здравствуйте, blacksun, Вы писали:


B>
B>using namespace std;

B>class A
B>{
B>public:

B>    A()
B>    {
B>        func2();
B>    }

B>    void func1()
B>    {
B>        func2();
B>    }

B>    virtual void func2()
B>    {
B>        cout << "A func2" << endl;
B>    }
B>};


B>class B : public A
B>{
B>public:
B>    void func2()
B>    {
B>        cout << "B func2" << endl;
B>    }
B>};


B>int _tmain(int argc, _TCHAR* argv[])
B>{

B>    B b;
B>    b.func1();

B>    A *aa = dynamic_cast<A *>(&b);
    aa->>func1();

B>    return 0;
B>}
B>



B>При запуске программы, имеем следующий вывод:


B>

B>A func2
B>B func2
B>B func2


B>Можно прокомментировать всю подноготную вопроса??

B>Я понимаю следующим образом:
B>С первой строкой все ясно, вызвалась из конструктора A, B еще и помине нет, поэтому "B func2" мы по любому бы никогда не увидели
B>Со второй тоже вроде как ясно. Вызываем унаследованный func1, он пытается вызвать func2, происходит поиск по таблице виртуальных функций обьекта B, находится перегруженный func2 и вызывается именно он.
B>А во с третьей строкой непонятно. Почему так??


Советую вам почитать про VPTR и VTABLE, о том, в какой момент они и как заполняются. Если кратко, то VTABLE заполняется в конструкторе класса и соот-но если в классе есть перегруженная функция, то в VTABLE заносится ее адрес для текущего класса и так далее по иерархии... Соот-но в итоге у вас есть указатель, который указывает на какого-то наследника, то как бы вы его не приводили(повышающее приведение или понижающее в случае указателя на какого-то из предков) конструктор соот-но уже не вызывается и таблица VTABLE такая, какой она была после вызова последнего конструктора в иерархии. Когда вы явно вызываете dynamic_cast, не забывайте проверять на 0 (NULL в VS). И собственно полиморфизм как раз работает через ссылки и указатели, поэтому если вы правильно приводите все, то будет все будет ок.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.