Вызов виртуальной функции из базового класса
Скажем, что у нас есть:
Class Base
{
virtual void f(){g();};
virtual void g(){//Do some Base related code;}
};
Class Derived : public Base
{
virtual void f(){Base::f();};
virtual void g(){//Do some Derived related code};
};
int main()
{
Base *pBase = new Derived;
pBase->f();
return 0;
}
Какой g()
будет вызываться из Base::f()
? Base::g()
или Derived::g()
?
Спасибо...
Ответы
Ответ 1
Будет вызываться g производного класса. Если вы хотите вызвать функцию в базе, вызовите
Base::g();
вместо этого. Если вы хотите вызвать производную, но все же хотите вызвать базовую версию, установите, что производная версия g вызывает базовую версию в ее первом выражении:
virtual void g() {
Base::g();
// some work related to derived
}
Тот факт, что функция из базы может вызывать виртуальный метод и управление передается в производный класс, используется в шаблоне проектирования шаблона метода. Для С++ он более известен как Non-Virtual-Interface. Он широко используется также в стандартной библиотеке С++ (например, буферы потоков С++ имеют функции pub...
, которые вызывают виртуальные функции, которые выполняют реальную работу. Например, pubseekoff
вызывает защищенный seekoff
). Я написал пример этого в этом ответе: Как вы проверяете внутреннее состояние объектов?
Ответ 2
pBase - это указатель на базу.
pBase = new Derived возвращает указатель на Derived - Derived is-a Base.
Итак, pBase = new Производится.
pBase ссылается на базу, поэтому он будет смотреть на Derived, как на базовую.
pBase- > f() вызовет Derive:: f();
Затем мы видим в коде, что:
Derive:: f() → Base:: f() → g() - но который g??
Ну, он вызывает Derive:: g(), потому что это g, который pBase "указывает" на.
Ответ: Derive:: g()
Ответ 3
Это Derived:: g, если вы не назовете g в базовом конструкторе. Поскольку базовый конструктор вызывается до создания объекта Derived, Derived:: g не может быть логически вызван, потому что он может манипулировать переменными, которые еще не были созданы, поэтому вызывается Base:: g.
Ответ 4
Ну... Я не уверен, что это должно скомпилироваться. Следующее,
Base *pBase = new Derived;
недействителен, если у вас нет:
Class Derived : public Base
Он хочет, чтобы вы имели в виду? Если это нужно, вы имели в виду,
pBase->f();
Тогда стек вызовов будет выглядеть следующим образом:
Derived::f()
Base::f()
Derived::g()
Ответ 5
Как вы определили g() как виртуальную, наиболее выведенный g() будет искать в vtable класса и вызывать независимо от типа, к которому ваш код в настоящее время обращается к нему.
См. Часто задаваемые вопросы по С++ для виртуальных функций.
Ответ 6
На самом деле запуск вашего кода показывает, что вызывается Derived:: g().
Ответ 7
Я думаю, вы пытаетесь придумать Шаблон метода шаблона
Ответ 8
Вызывается метод производного класса.
Это связано с включением vtables в классы, которые имеют виртуальные функции и классы, которые переопределяют эти функции. (Это также называется динамической рассылкой.) Вот что происходит: vtable создается для Base
, а vtable создается для Derived
, потому что для каждого класса есть только один vtable. Поскольку pBase
вызывает функцию, которая является виртуальной и переопределенной, вызывается указатель на vtable для Derived
. Назовите его d_ptr
, также известный как vpointer:
int main()
{
Base *pBase = new Derived;
pBase->d_ptr->f();
return 0;
}
Теперь d_ptr вызывает Derived::f()
, который вызывает Base::f()
, который затем смотрит на vtable, чтобы увидеть, что использовать g()
. Поскольку vpointer знает только g()
в Derived
, тот, который мы используем. Поэтому вызывается Derived::g()
.