views:

280

answers:

5

Hi!

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.

+2  A: 

Because technically it is virtual whatever you do — it has its place in the table. The rest would be a syntactical law enforcement and this is where C++ is different from java.

Michael Krelin - hacker
+6  A: 

When you declare a virtual method, you're basically adding a new entry in the vtable. Overriding a virtual method changes the value of that entry; it doesn't remove it. This is basically true for languages like Java or C# too. The difference is that, with final keyword in Java, you can ask the compiler to arbitrarily enforce not being able to override it. C++ does not provide this language feature.

Mehrdad Afshari
I thought that C++0x was looking at including something like final methods. Also, as a minor aside, while C++ currently doesn't support `final` methods - you can effectively seal a class by making its constructor private and exposing a static factory method to instantiate it.
LBushkin
+1  A: 

A vtable is created for the base class when the first virtual function is defined. In your example foo() has an entry in the vtable. When the derived class inherits from the base class it inherits the vtable as well. The derived class must have an entry for foo() in its vtable so that the call will be redirected appropriately when the derived class is referenced polymorphically through a base class pointer.

William Bell
+5  A: 

Just because the class is forced to have a vtable, doesn't mean the compiler is forced to use it. If the type of the object is known statically, the compiler is free to bypass the vtable as an optimization. For example, B::foo will probably be called directly in this situation:

B b;
b.foo();

Unfortunately the only way I know to verify this is to look at the generated assembly code.

Mark Ransom
AFAIK, this answer is wrong. Calling B::foo directly is against the standard.
kotlinski
Where in the standard does it specify that B::foo cannot be called directly in this example? I don't even quite see how the standard can forbid it - b cannot possibly belong to any derived class of B, so surely the "as-if" rule permits compilers to do anything they like, as long as the right function is called. Since the compiler knows that function is B::foo, and none other, why bother calling virtually?
Steve Jessop
OK, I am wrong :)Problem is if I have a reference or a pointer.
kotlinski
+7  A: 

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.

Steve Jessop