C++ 重写虚拟成员函数时,为什么重写函数总是变为虚拟的?

C++ 重写虚拟成员函数时,为什么重写函数总是变为虚拟的?,c++,inheritance,virtual,overriding,C++,Inheritance,Virtual,Overriding,当我这样写的时候: class A { public: virtual void foo() = 0; } class B { public: void foo() {} } …B::foo()也变成虚拟的。这背后的理由是什么?我希望它的行为类似于Java中的final关键字 Ad:我知道这样的工作方式和VTALE是如何工作的:问题是,为什么C++标准委员会没有留下一个直接调用B::FoE()的选项,避免了VTABLE查找。< P>因为技术上无论您做什么,它都是虚拟的,它在

当我这样写的时候:

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

class B {
    public: void foo() {}
}
…B::foo()也变成虚拟的。这背后的理由是什么?我希望它的行为类似于Java中的
final
关键字


Ad:我知道这样的工作方式和VTALE是如何工作的:问题是,为什么C++标准委员会没有留下一个直接调用B::FoE()的选项,避免了VTABLE查找。

< P>因为技术上无论您做什么,它都是虚拟的,它在表中占有一席之地。其余的将是一个语法强制执行,这是C++与java不同的地方。

当你声明一个代码>虚拟< /C> >方法时,你基本上在VTABLE中添加了一个新条目。重写
virtual
方法会更改该条目的值;它不会移除它。对于Java或C#等语言,这基本上也是如此。不同之处在于,使用Java中的
final
关键字,您可以要求编译器任意强制执行,但不能覆盖它。C++没有提供这种语言特性。

< P>当定义第一个虚函数时,为基类创建了一个VTABLE。在您的示例中,foo()在vtable中有一个条目。当派生类从基类继承时,它也继承vtable。派生类的vtable中必须有foo()的条目,这样当派生类通过基类指针以多态方式引用时,调用将被适当重定向。

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

B b;
b.foo();

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

该标准确实留下了一个直接调用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();
}
因此,您可以得到您所期望的行为,如下所示:

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(); }
};
A类{
虚拟void foo()=0;
//对foo进行虚拟调用
public:void bar(){foo();}
};
B类:公共A{
void foo(){

std::cout似乎至少Visual Studio能够利用
final
关键字跳过vtable查找,例如以下代码:

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();
b.foo()
b.b::foo()
生成相同的代码:

而如果没有
final
,则使用查找表:

        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)

我不知道其他编译器是否也这么做。

我认为C++ 0x查看的内容包括一些最终方法。另外,作为一个小节,C++目前不支持<代码>最终< /Cord>方法——可以通过使构造函数私有化,并公开静态工厂方法来实例化I来有效地密封类。t、 好吧,这个答案是错误的。直接调用B::foo是违反标准的。在这个例子中,标准中哪里规定不能直接调用B::foo?我甚至不太明白标准是如何禁止它的-B不可能属于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)
        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)