English 中文(简体)
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.

最佳回答

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

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

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
";
    }
    // 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.

问题回答

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.

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.

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.

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.

It seems that at least Visual Studio is able to take advantage of the final keyword to skip vtable lookup, eg this code:

class A {
public:
    virtual void foo() = 0;
};
class B : public A {
public:
    void foo() final {}
};
B original;
B& b = original;
b.foo();
b.B::foo();

Produces the same code for b.foo() and for b.B::foo():

        b.foo();
000000013F233AA9  mov         rcx,qword ptr [b]
000000013F233AAE  call        B::foo (013F1B4F48h)
        b.B::foo();
000000013F233AB3  mov         rcx,qword ptr [b]
000000013F233AB8  call        B::foo (013F1B4F48h)

Whereas without the final it uses the lookup table:

        b.foo();
000000013F893AA9  mov         rax,qword ptr [b]
000000013F893AAE  mov         rax,qword ptr [rax]
000000013F893AB1  mov         rcx,qword ptr [b]
000000013F893AB6  call        qword ptr [rax]
        b.B::foo();
000000013F893AB8  mov         rcx,qword ptr [b]
000000013F893ABD  call        B::foo (013F814F48h)

I don t know whether other compilers do the same, though.





相关问题
Undefined reference

I m getting this linker error. I know a way around it, but it s bugging me because another part of the project s linking fine and it s designed almost identically. First, I have namespace LCD. Then I ...

C++ Equivalent of Tidy

Is there an equivalent to tidy for HTML code for C++? I have searched on the internet, but I find nothing but C++ wrappers for tidy, etc... I think the keyword tidy is what has me hung up. I am ...

Template Classes in C++ ... a required skill set?

I m new to C++ and am wondering how much time I should invest in learning how to implement template classes. Are they widely used in industry, or is this something I should move through quickly?

Print possible strings created from a Number

Given a 10 digit Telephone Number, we have to print all possible strings created from that. The mapping of the numbers is the one as exactly on a phone s keypad. i.e. for 1,0-> No Letter for 2->...

typedef ing STL wstring

Why is it when i do the following i get errors when relating to with wchar_t? namespace Foo { typedef std::wstring String; } Now i declare all my strings as Foo::String through out the program, ...

C# Marshal / Pinvoke CBitmap?

I cannot figure out how to marshal a C++ CBitmap to a C# Bitmap or Image class. My import looks like this: [DllImport(@"test.dll", CharSet = CharSet.Unicode)] public static extern IntPtr ...

Window iconification status via Xlib

Is it possible to check with the means of pure X11/Xlib only whether the given window is iconified/minimized, and, if it is, how?

热门标签