调用非虚函数的虚函数

Virtual function calling a non-virtual function

本文关键字:函数 调用      更新时间:2023-10-16

我写了以下一段代码来测试我对虚拟继承的理解。显然,我仍然没有完全理解它。这是我的代码(后面跟着我的问题(:

#include <iostream>
#include <vector>
using namespace std;
class Foo
{
public:
    virtual void foo();
    void foo2();
};
void Foo::foo()
{
    cout << "In FOO - foo 1" << endl;
    foo2();
}
void Foo::foo2()
{
    cout << "In FOO - foo 2" << endl;
}
class Bar : public Foo
{
public:
    void foo();
    void foo2();
};
void Bar::foo()
{
    cout << "In BAR - foo 1" << endl;
    foo2();
}
void Bar::foo2()
{
    cout << "In BAR - foo 2" << endl;
}
int main()
{
    Foo* f = new Foo;
    f->foo();
    Foo* b = new Bar;
    b->foo();
    return 0;
}

这是我的理解:

指针 f 指向基类 Foo,f->foo()调用基类中的foo(),基类又调用基类中的foo2()

指针 b 是基类指针,但指向派生类 Bar 的对象。现在,由于foo()是一个虚函数,因此它调用派生类foo()。现在(派生类的(foo()调用foo2() 。由于foo2()不是虚函数,我期待基类foo2()被调用。但是,我看到foo2()派生类都被调用了。

所以,我期待这个输出:

In FOO - foo 1
In FOO - foo 2
In BAR - foo 1
In FOO - foo 2

但得到了这个:

In FOO - foo 1
In FOO - foo 2
In BAR - foo 1
In BAR - foo 2

为什么会这样?据我了解,vtable 将有一个仅用于foo()的条目,而不是用于foo2()的条目。那么,如何调用派生类foo2()呢?

这是我的第一篇文章。如果我违反了任何发布准则,请原谅我。提前感谢!

Bar::foo()中,您正在调用foo2()。 这真的相当于叫this->foo2()this的类型是Bar,所以这实际上等同于:

void Bar::foo()
{
    Bar *bar = this;
    bar->foo2();
}

因此,此时不涉及多态性;调用解析为在编译时Bar::foo2,而不是在运行时动态

因为 Bars foo 就是所谓的 foo2,在那个阶段它知道它是一个 Bar....

再试一次,除了直接在 main 中调用 foo2 而不是 foo 调用 foo2

int main()
{
    Foo* f = new Foo;
    f->foo();
    f->foo2();
    Foo* b = new Bar;
    b->foo();
    b->foo2();
    return 0;
}

如何调用派生类的 FOO2((?

你希望 Bar::foo2 永远不会被调用。然后,您的问题可以重新表述为:"为什么Bar::foo2的目的是,如果有的话?

其目的是在处理 Bar 对象时调用。每当调用 foo2 时,都会调用 Bar 对象的 foo2。

只有对于 foo 对象,Foo::bar2 是否是虚拟的才重要。 如果您直接处理派生类对象,继承永远不会强制您在基类中使用具有相同签名的函数。(在这件事上,继承规则以不同的方式工作会导致太多不愉快的意外。

你所做的本质上是隐藏。通过在 Bar 中创建具有相同签名的函数,您将非虚函数隐藏在基类 Foo 中。这通常是一个糟糕的设计,因为它不必要地复杂 - 最好为不同的东西选择不同的名称,以避免隐藏。隐藏很少是有意识的良好设计的一部分。

void Bar::foo()
{
    cout << "In BAR - foo 1" << endl;
    foo2();
}

这是因为Bar::foo2()foo()调用的foo2()Bar::foo2()Bar::foo()的本地,具有优先权。这只是来自Bar::foo2()的静态调度.

如果您期望的行为是您真正想要的,则可以通过指定其范围来选择该方法,如下所示:

void Bar::foo()
{
    cout << "In BAR - foo 1" << endl;
    Foo::foo2();
}

所以这真的与动态调度无关。