覆盖虚拟成员函数时,为什么覆盖函数总是变为虚拟?

Joh*_*ski 4 c++ virtual inheritance overriding

我写这样的时候:

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

class B {
    public: void foo() {}
}
Run Code Online (Sandbox Code Playgroud)

... B :: foo()也变为虚拟.这背后的理由是什么?我希望它的行为类似于finalJava中的关键字.

补充:我知道这样的工作方式和vtable如何工作:)问题是,为什么C++标准委员会没有留下开放直接调用B :: foo()并避免vtable查找.

Ste*_*sop 9

该标准确实留下了一个直接调用B :: foo的开口并避免了表查找:

#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();
}
Run Code Online (Sandbox Code Playgroud)

输出:

C::foo
C::foo
B::foo
Run Code Online (Sandbox Code Playgroud)

因此,您可以获得您所期望的行为,如下所示:

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(); }
};
Run Code Online (Sandbox Code Playgroud)

现在调用者应该使用bar而不是foo.如果他们有C*,那么他们可以将它转换为A*,在这种情况下bar会调用C::foo,或者他们可以将其转换为B*,在这种情况下bar会调用B::foo.如果需要,C可以再次覆盖bar,否则不会打扰,在这种情况下bar(),可以B::foo()按照您的预期调用C*调用.

但我不知道何时会有人想要这种行为.虚函数的重点是为给定对象调用相同的函数,无论您使用的是什么基类或派生类指针.因此,C++假定如果通过基类对特定成员函数的调用是虚拟的,那么通过派生类的调用也应该是虚拟的.


Meh*_*ari 6

声明virtual方法时,基本上是在vtable中添加新条目.覆盖virtual方法会更改该条目的值; 它不会删除它.对于Java或C#等语言来说基本上也是如此.区别在于,使用finalJava中的关键字,您可以要求编译器任意强制执行不能覆盖它.C++不提供此语言功能.


Mar*_*som 5

仅仅因为该类被强制使用vtable,并不意味着编译器被迫使用它.如果对象的类型是静态已知的,则编译器可以自由地绕过vtable作为优化.例如,在这种情况下可能会直接调用B :: foo:

B b;
b.foo();
Run Code Online (Sandbox Code Playgroud)

不幸的是,我知道验证这一点的唯一方法是查看生成的汇编代码.