虚函数调用非虚函数

时间:2012-01-09 23:24:44

标签: c++ inheritance virtual

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

#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()

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

4 个答案:

答案 0 :(得分:8)

Bar::foo(),您正在呼叫foo2()。这实际上相当于调用this->foo2()this的类型为Bar,因此这实际上等同于:

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

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

答案 1 :(得分:1)

因为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;
}

答案 2 :(得分:1)

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

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

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

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

所以这与动态调度无关。

答案 3 :(得分:1)

  

如何调用派生类的foo2()?

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

在处理Bar对象时调用它的目的。只要调用foo2,就会调用Bar对象的foo2。

对于foo对象,Foo :: bar2是否为虚拟才重要。如果直接处理派生类对象,继承永远不会强制您在基类中使用具有相同签名的函数。 (如果继承规则在这个问题上有所不同,那会导致太多令人不快的意外。)

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