2012-01-09 4 views
2

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

#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 является указателем базового класса, но указывает на объект класса производного класса. Теперь, поскольку 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() производный класс?

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

+1

Добавьте вызов 'b-> foo2()' чуть ниже 'b-> foo();', посмотрите, что произойдет. Это то, чего вы ожидаете? – Useless

+0

Да. Это то, чего я ожидаю. Благодаря ответам я теперь понимаю, что происходит :) – Vinay

+1

Примечание: ваш код не имеет * виртуального наследования *. Он имеет обычное однонамерное наследование из полиморфного класса. Виртуальное наследование - это класс X: public virtual Y {}; ' – ybungalobill

ответ

8

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

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

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

+0

Получил это. Благодаря! – Vinay

1

потому Bars Foo является то, что называя foo2, на этом этапе она знает его Баром ....

попробовать еще раз, за ​​исключением вызова Foo2 непосредственно в основном для обоих, а не Foo вызова foo2

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

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

    return 0; 
} 
+0

Получил это. Благодаря! – Vinay

1
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(); 
} 

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

+0

Получил это. Благодаря! – Vinay

1

Как происходит вызов foo2() вызываемого производного класса?

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

Его назначение при вызове объектов Bar. Foo2 объекта Bar вызывается всякий раз, когда вызывается foo2.

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

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

+1

Получил это. Благодаря! – Vinay

Смежные вопросы