#c #casting #dynamic-cast #static-cast
Вопрос:
Я начал узнавать о динамическом приведении и о том, как он использует RTTI для определения типа объекта для понижения. В этом примере я сделал переход от производного класса к базовому классу.
#include <iostream>
using namespace std;
class Base
{
public:
virtual void foo()
{
cout<<"Base"<<endl;
}
};
class Derived : public Base
{
public:
void foo()
{
cout<<"Derived"<<endl;
}
};
int main()
{
Derived* d = new Derived();
Base* b = dynamic_cast<Base*>(d); //(1)
cout<<typeid(b).name()<<endl;
b->foo();
return 0;
}
Программа печатает:
PBase
Derived
Для меня:
- В компиляции компилятор создает таблицу V для базового класса, где у нас есть указатель на «таблицу RTTI» и указатель на функцию Base::foo.
- Во время выполнения, когда программа доходит до строки (1), он создает указатель _vptrBase, который указывает на эту виртуальную таблицу.
Мой вопрос: как получается, что программа выводит «Производное», когда мы вызываем функцию foo? Почему он не вызывает функцию Base::foo?
Комментарии:
1.
Base* b = dynamic_cast<Base*>(d);
равносильно справедливомуBase* b = d;
. Эта строка не касается указателя vtable внутри класса. Он указывал наDerived
таблицу v, когда объект был создан (потому что это его тип), и он продолжает указывать на эту таблицу v после приведения.2. если вы этого не хотите, то не делайте
foo
виртуальным. смотрите в прямом эфире3. если вы действительно хотите виртуальный, но вам нужен вызов
Base::foo
, то вызовите его явноb->Base::foo();
. смотрите в прямом эфире4. Для этого и нужен полиморфизм. У вас может быть указатель на базовый класс и методы вызова, определенные для производных версий. Лучшим примером может быть массив базовых указателей и несколько версий производных классов.
Ответ №1:
Цитирую cppreference (выделено жирным шрифтом):
Виртуальные функции-это функции-члены, поведение которых может быть переопределено в производных классах. В отличие от невиртуальных функций, поведение переопределения сохраняется, даже если во время компиляции отсутствует информация о фактическом типе класса. То есть, если производный класс обрабатывается с использованием указателя или ссылки на базовый класс, вызов переопределенной виртуальной функции вызовет поведение, определенное в производном классе. Такой вызов функции известен как вызов виртуальной функции или виртуальный вызов.