Virtual function calling a non-virtual function

2019-05-18 09:05发布

问题:

I wrote the following piece of code to test my understanding of virtual inheritance. Apparently, I still don't get it fully. Here is my code (followed by my question):

#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;
}

This is my understanding:

The pointer f points to the base class Foo and f->foo() calls foo() in the base class which in turn calls foo2() in the base class.

The pointer b is a base-class pointer but points to an object of the derived class Bar. Now, since foo() is a virtual function, it calls foo() of the derived class. Now foo() (of the derived class) calls foo2(). Since foo2() is not a virtual function, I was expecting the base class foo2() to get called. However I see that foo2() of the derived class is getting called.

So, I was expecting this output:

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

but got this instead:

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

Why is this so? From what I understand, the vtable will have an entry only for foo() and not for foo2(). So, how is foo2() of the derived class getting called?

This is my first post. Please excuse me if I have broken any posting guidelines. Thanks in advance!

回答1:

In Bar::foo(), you are calling foo2(). This is really equivalent to calling this->foo2(). The type of this is Bar, so this is really equivalent to:

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

So there's no polymorphism involved at this point; the call is resolved to Bar::foo2 at compile-time, rather than dynamically at run-time.



回答2:

because Bars foo is what's calling foo2, at that stage it knows its a Bar....

try again, except call foo2 directly in main for both rather than foo calling foo2

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

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

    return 0;
}


回答3:

how is foo2() of the derived class getting called?

You expect Bar::foo2 to never be called. Then your question can be reformulated as: "why is the purpose of Bar::foo2, if any?"

Its purpose is to be called when dealing with Bar objects. The foo2 of a Bar object is called whenever foo2 is called.

It is only for foo objects that it matters whether Foo::bar2 is virtual or not. Inheritance never forces you to use the functions with the same signature in the base class, if you are dealing directly with derived class objects. (It would cause too many unpleasant surprises to have the inheritance rules work differently in this matter.)

What you have essentially done is hiding. By creating a function with the same signature in Bar, you have hidden the nonvirtual function in the base class Foo. This is typically a bad design because it is unnecessarily complex - it is better to choose different names for different things, to avoid hiding. Hiding is seldom part of a conscious good design.



回答4:

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

This is because Bar::foo2() is the foo2() called by foo(). Bar::foo2() is local to Bar::foo() and has precedence. It's just static dispatch from Bar::foo2().

If the behavior you expected is what you really want, you can choose the method by specifying its scope like so:

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

So this really does not have to do with dynamic dispatch.