вызов виртуальной функции из базового класса - PullRequest
47 голосов
/ 29 декабря 2008

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


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

Спасибо ...

Ответы [ 8 ]

52 голосов
/ 29 декабря 2008

Будет вызван г производного класса. Если вы хотите вызвать функцию в базе, позвоните

Base::g();

вместо этого. Если вы хотите вызвать производную, но по-прежнему хотите вызывать базовую версию, договоритесь о том, чтобы производная версия g вызывала базовую версию в своем первом операторе:

virtual void g() {
    Base::g();
    // some work related to derived
}

Тот факт, что функция из базы может вызывать виртуальный метод и управление передается в производный класс, используется в шаблоне разработки метода шаблона. Для C ++ он более известен как Non-Virtual-Interface . Он также широко используется в стандартной библиотеке C ++ (например, потоковые буферы C ++ имеют функции pub..., которые вызывают виртуальные функции, выполняющие реальную работу. Например, pubseekoff вызывает защищенный seekoff). Я написал пример этого в этом ответе: Как вы проверяете внутреннее состояние объекта?

6 голосов
/ 19 сентября 2013

Это Derived :: g, если вы не вызываете g в конструкторе Base. Поскольку конструктор Base вызывается до создания объекта Derived, Derived :: g не может быть вызван логически, поскольку он может манипулировать переменными, которые еще не были созданы, поэтому будет вызываться Base :: g.

5 голосов
/ 29 мая 2010

pBase - указатель на базу. pBase = new Derived возвращает указатель на Derived - Derived is-a Base.

Итак, pBase = new Derived действителен.

pBase ссылается на базу, поэтому он будет выглядеть как производный, как если бы это была база.

pBase-> f () вызовет Derive :: f ();

Тогда мы видим в коде, что:

Derive :: f () -> Base :: f () -> g () - но какой g ??

Ну, он вызывает Derive :: g (), потому что это g, на который "указывает" pBase.

Ответ: Derive :: g ()

2 голосов
/ 29 декабря 2008

Ну ... я не уверен, что это должно скомпилироваться. Следующее

Base *pBase = new Derived;

недействительно, если у вас нет:

Class Derived : public Base

Хочешь, чтобы ты имел в виду? Если вы хотите, чтобы вы имели в виду,

pBase->f();

Тогда стек вызовов будет выглядеть так:

Derived::f()
    Base::f()
        Derived::g()
1 голос
/ 30 декабря 2008

Фактически выполнение вашего кода показывает, что Derived :: g () вызывается.

1 голос
/ 29 декабря 2008

Поскольку вы определили g () как виртуальный, наиболее производный g () будет найден в виртуальной таблице класса и вызван независимо от типа, к которому ваш код обращается в данный момент.

См. C ++ FAQ по виртуальным функциям .

0 голосов
/ 23 июня 2016

Будет вызван метод производного класса.

Это происходит из-за включения vtables в классы, которые имеют виртуальные функции, и классы, которые переопределяют эти функции. (Это также называется динамической диспетчеризацией.) Вот что на самом деле происходит: создается виртуальная таблица для Base, а виртуальная таблица создается для Derived, потому что в классе есть только одна виртуальная таблица. Поскольку pBase вызывает виртуальную и переопределенную функцию, вызывается указатель на виртуальную таблицу для 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() называется.

0 голосов
/ 29 декабря 2008

Я думаю, вы пытаетесь изобрести Шаблонный метод Pattern

...