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 и вызывается именно он.
А во с третьей строкой непонятно. Почему так??
Здравствуйте, glap, Вы писали:
G>Так в 3-м ничего не изменилось. Всё как и во втором. Ищет в таблице и находит.
Но в третьем случае мы типа как с объектом класса А работаем. Т.е. по идее надо бы ограничить эти поиски классом А и базовыми для А, если они есть. Я просто не понимаю до конца.
Здравствуйте, 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();
Иначе (если всегда нужно знать точный тип), чем виртуальная функция отличалась бы от невиртуальной?
Здравствуйте, blacksun, Вы писали:
B>Здравствуйте, glap, Вы писали:
G>>Так в 3-м ничего не изменилось. Всё как и во втором. Ищет в таблице и находит.
B>Но в третьем случае мы типа как с объектом класса А работаем. Т.е. по идее надо бы ограничить эти поиски классом А и базовыми для А, если они есть. Я просто не понимаю до конца.
Таблица виртуальных методов всегда одна и оттого, что ты сделал приведение к базовому классу с ней ничего не случится.
В этом ведь вся соль полиморфизма, чтоб ты мог обращаться к методам реально созданного класса независимо от типа указателя.
G>Таблица виртуальных методов всегда одна и оттого, что ты сделал приведение к базовому классу с ней ничего не случится. G>В этом ведь вся соль полиморфизма, чтоб ты мог обращаться к методам реально созданного класса независимо от типа указателя.
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>В этом ведь вся соль полиморфизма, чтоб ты мог обращаться к методам реально созданного класса независимо от типа указателя.
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. И на этом этапе таблицы виртуальных функций как-то теряются)))
Во втором случае передается по ссылке, по сути по указателю, соответственно объект тот же и все полиморфные фишки работают.
Здравствуйте, blacksun, Вы писали:
B>А можно пояснить подробней?? Я так понимаю, в первом случае в обработчик кетч передается обьект по значению, т.е. его копия, соответственно вызывается конструктор копирования A. И на этом этапе таблицы виртуальных функций как-то теряются))) 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). И собственно полиморфизм как раз работает через ссылки и указатели, поэтому если вы правильно приводите все, то будет все будет ок.