【问题标题】:Virtual function calling a non-virtual function虚函数调用非虚函数
【发布时间】:2012-02-06 11:20:00
【问题描述】:

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

#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-&gt;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()是如何被调用的呢?

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

【问题讨论】:

  • b-&gt;foo();下方添加对b-&gt;foo2()的调用,看看会发生什么。是你所期望的吗?
  • 是的。这就是我所期望的。感谢答案,我现在明白发生了什么:)
  • 注意:您的代码没有虚拟继承。它通常具有来自多态类的单一继承。虚拟继承是class X : public virtual Y { };
  • 谢谢 ybungalobill。我将阅读更多关于这个主题的内容,并更彻底地理解其中的区别。

标签: c++ inheritance virtual


【解决方案1】:

Bar::foo() 中,您正在调用foo2()。这真的相当于调用this-&gt;foo2()this 的类型是Bar,所以这真的等价于:

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

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

【讨论】:

    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

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

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

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

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

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

      【讨论】:

        【解决方案4】:
        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();
        }
        

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

        【讨论】:

          猜你喜欢
          • 2015-09-17
          • 2021-07-07
          • 1970-01-01
          • 1970-01-01
          • 2015-04-20
          • 2020-07-02
          • 1970-01-01
          • 1970-01-01
          • 2012-01-28
          相关资源
          最近更新 更多