Вызов виртуальной функции из базового класса

Скажем, что у нас есть:


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()

Ответ 6

На самом деле запуск вашего кода показывает, что вызывается Derived:: g().

Ответ 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().