Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When overriding a virtual member function, why does the overriding function always become virtual?

When I write like this:

class A {
    public: virtual void foo() = 0;
}

class B {
    public: void foo() {}
}

...B::foo() becomes virtual as well. What is the rationale behind this? I would expect it to behave like the final keyword in Java.

Add: I know that works like this and how a vtable works :) The question is, why C++ standard committee did not leave an opening to call B::foo() directly and avoid a vtable lookup.

like image 942
Johan Kotlinski Avatar asked Nov 29 '22 20:11

Johan Kotlinski


1 Answers

The standard does leave an opening to call B::foo directly and avoid a table lookup:

#include <iostream>

class A {
    public: virtual void foo() = 0;
};

class B : public A {
    public: void foo() {
        std::cout <<"B::foo\n";
    }
};

class C : public B {
    public: void foo() {
        std::cout <<"C::foo\n";
    }
};

int main() {
    C c;
    A *ap = &c;
    // virtual call to foo
    ap->foo();
    // virtual call to foo
    static_cast<B*>(ap)->foo();
    // non-virtual call to B::foo
    static_cast<B*>(ap)->B::foo();
}

Output:

C::foo
C::foo
B::foo

So you can get the behaviour you say you expect as follows:

class A {
    virtual void foo() = 0;
    // makes a virtual call to foo
    public: void bar() { foo(); }
};

class B : public A {
    void foo() {
        std::cout <<"B::foo\n";
    }
    // makes a non-virtual call to B::foo
    public: void bar() { B::foo(); }
};

Now callers should use bar instead of foo. If they have a C*, then they can cast it to A*, in which case bar will call C::foo, or they can cast it to B*, in which case bar will call B::foo. C can override bar again if it wants, or else not bother, in which case calling bar() on a C* calls B::foo() as you'd expect.

I don't know when anyone would want this behaviour, though. The whole point of virtual functions is to call the same function for a given object, no matter what base or derived class pointer you're using. C++ therefore assumes that if calls to a particular member function through a base class are virtual, then calls through derived classes should also be virtual.

like image 180
Steve Jessop Avatar answered Dec 04 '22 09:12

Steve Jessop