当我这样写时:

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

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

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

添加:我知道它是这样工作的,以及 vtable 是如何工作的 :) 问题是,为什么 C++ 标准委员会没有留下直接调用 B::foo() 并避免 vtable 查找的机会。

最佳答案

该标准确实留下了直接调用 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();
}

输出:
C::foo
C::foo
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(); }
};

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

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

关于c++ - 覆盖虚成员函数时,为什么覆盖的函数总是变成虚函数?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/1929209/

10-11 22:07
查看更多