Виртуальная функция, вызывающая не виртуальную функцию - PullRequest
2 голосов
/ 10 января 2012

Я написал следующий фрагмент кода, чтобы проверить мое понимание виртуального наследования. Очевидно, я все еще не понимаю это полностью. Вот мой код (за которым следует мой вопрос):

#include <iostream>
#include <vector>

using namespace std;

class Foo
{
public:
    virtual void foo();
    void foo2();
};

void Foo::foo()
{
    cout << "In FOO - foo 1" << endl;
    foo2();
}

void Foo::foo2()
{
    cout << "In FOO - foo 2" << endl;
}

class Bar : public Foo
{
public:
    void foo();
    void foo2();
};

void Bar::foo()
{
    cout << "In BAR - foo 1" << endl;
    foo2();
}

void Bar::foo2()
{
    cout << "In BAR - foo 2" << endl;
}

int main()
{
    Foo* f = new Foo;
    f->foo();

    Foo* b = new Bar;
    b->foo();

    return 0;
}

Это мое понимание:

Указатель f указывает на базовый класс Foo, а f->foo() вызывает foo() в базовом классе, который, в свою очередь, вызывает foo2() в базовом классе.

Указатель b является указателем базового класса, но указывает на объект производного класса Bar. Теперь, поскольку foo() является виртуальной функцией, она вызывает foo() производного класса. Теперь foo() (из производного класса) вызывает foo2(). Поскольку foo2() не является виртуальной функцией, я ожидал, что базовый класс foo2() будет вызван. Однако я вижу, что foo2() производного класса вызывается.

Итак, я ожидал этого вывода:

In FOO - foo 1
In FOO - foo 2
In BAR - foo 1
In FOO - foo 2

но получил вместо этого:

In FOO - foo 1
In FOO - foo 2
In BAR - foo 1
In BAR - foo 2

Почему это так? Насколько я понимаю, vtable будет иметь запись только для foo(), а не для foo2(). Итак, как вызывается foo2() производного класса?

Это мой первый пост. Пожалуйста, извините, если я нарушил правила публикации. Заранее спасибо!

Ответы [ 4 ]

8 голосов
/ 10 января 2012

В Bar::foo() вы звоните foo2(). Это действительно эквивалентно звонку this->foo2(). Тип this равен Bar, так что это действительно эквивалентно:

void Bar::foo()
{
    Bar *bar = this;
    bar->foo2();
}

Так что в этот момент не происходит полиморфизма; вызов разрешается до Bar::foo2 во время компиляции, а не динамически во время выполнения.

1 голос
/ 10 января 2012

как вызывается функция foo2 () из производного класса?

Вы ожидаете, что Bar :: foo2 никогда не будет вызываться. Тогда ваш вопрос можно переформулировать так: «почему цель Bar :: foo2, если таковая имеется?»

Его цель - вызываться при работе с объектами Бар. Foo2 объекта Bar вызывается всякий раз, когда вызывается foo2.

Только для объектов foo имеет значение, является ли Foo :: bar2 виртуальным или нет. Наследование никогда не заставляет вас использовать функции с одинаковой сигнатурой в базовом классе, если вы имеете дело непосредственно с объектами производного класса. (Было бы слишком много неприятных сюрпризов, чтобы правила наследования работали по-другому в этом вопросе.)

То, что вы по существу сделали, это скрытие . Создав функцию с такой же сигнатурой в Bar, вы скрыли невиртуальную функцию в базовом классе Foo. Обычно это плохой дизайн, потому что он неоправданно сложен - лучше выбирать разные имена для разных вещей, чтобы избежать сокрытия. Сокрытие редко является частью сознательного хорошего дизайна.

1 голос
/ 10 января 2012
void Bar::foo()
{
    cout << "In BAR - foo 1" << endl;
    foo2();
}

Это потому, что Bar::foo2() - это foo2(), вызываемый foo(). Bar::foo2() локально для Bar::foo() и имеет приоритет. Это просто статическая отправка от Bar::foo2().

Если ожидаемое поведение соответствует желаемому, вы можете выбрать метод, указав его область действия так:

void Bar::foo()
{
    cout << "In BAR - foo 1" << endl;
    Foo::foo2();
}

Так что это действительно не имеет отношения к динамической диспетчеризации.

1 голос
/ 10 января 2012

потому что Bars foo - это то, что называется foo2, на этом этапе он знает, что это Bar ....

попробуйте еще раз, за ​​исключением вызова foo2 напрямую в main для обоих, вместо вызова foo foo2

int main()
{
    Foo* f = new Foo;
    f->foo();
    f->foo2();

    Foo* b = new Bar;
    b->foo();
    b->foo2();

    return 0;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...