【问题标题】:Which virtual method is executed in an inheritance hierarchy when intermediate classes skip the implementation?当中间类跳过实现时,在继承层次结构中执行哪个虚拟方法?
【发布时间】:2018-04-05 12:44:12
【问题描述】:

我确信之前有人问过这个问题,但我就是不知道要搜索什么。因此,一旦有人向我指出类似的问题,我很高兴删除这个问题。如果有人有更好的建议,我也很乐意重命名问题:-)

我想知道以下代码是否是由标准定义的行为,或者这是否可能取决于编译器/平台:

struct A
{
    virtual void f()
    {
        std::cout << "A::f()" << std::endl;
    }
};

struct B : public A
{
    // f() is not implemented here
};

struct C : public B
{
    virtual void f() override
    {
        B::f(); // call f() of direct base class although it is not implemented there
        std::cout << "C::f()" << std::endl;
    }
};

int main()
{
    A* pA = new C();
    pA->f();
}

Visual Studio 2017 和 gcc 5.4.0 的输出是:

A::f()
C::f()

编译器是否会在层次结构中向上搜索直到找到实现?你能链接到 C++ 标准吗?我通过在纯虚拟中制作 f() 对其进行了测试,链接器很好地告诉我有一个未解析的符号。我可以依赖它吗?

据我了解,使用像 B::f() 这样的范围运算符总是调用非虚拟版本。所以从来没有发生过多态性,是吗?

编辑:打印语句具有误导性,将“B::f()”替换为“C::f()”。

【问题讨论】:

  • 这称为虚拟或动态调度。这是维基的链接en.wikipedia.org/wiki/Dynamic_dispatch 还有另一个有趣的链接:condor.depaul.edu/ichu/csc447/notes/wk10/Dynamic2.htm
  • @AlexG 我认为动态调度意味着在层次结构中动态选择虚拟方法,这基本上可以实现多态性。我猜,查找非虚拟基础方法与此无关。
  • 这不是“非虚拟基础方法”。非虚拟将是“void foo()”。如果它被标记为虚拟,即使它属于基类,它也会被视为虚拟并创建一个 vftable。现在,如果子类没有覆盖或“替换”基类的方法,它将调用基类中的方法。我认为弗拉德在他的回答中解释得比我更好。
  • 你是对的,但我错过了在不调用多态性的情况下调用虚拟方法的词。我想这只是一个“直接方法调用”,就像我写的那样:B b; b.f(); // 也只是简单地调用 A::f() 而没有任何多态性。

标签: c++ class polymorphism virtual undefined-behavior


【解决方案1】:

指针的动态类型

A* pA = new C();

C *

所以调用类C中的虚函数

struct C : public B
{
    virtual void f() override
    {
        B::f(); // call f() of direct base class although it is not implemented there
        std::cout << "B::f()" << std::endl;
    }
};

B类没有重新定义基类A的虚函数。所以在这个声明中

        B::f(); // call f() of direct base class although it is not implemented there

类 A 中定义的虚函数被调用。也就是 B 类的虚函数的指针表包含了 A 类中定义的函数的地址。

在本次通话中

B::f();

可以访问 B 类的虚函数表,该表包含 A 类中函数定义的地址,因为该函数在 B 类中未被覆盖。

来自 C++ 标准(5.2.2 函数调用)

  1. ...如果所选函数是非虚函数,或者如果类成员访问表达式中的 id-expression 是限定 id,则该函数 叫做。否则,它的最终覆盖器(10.3)在动态类型 对象表达式被调用;这种呼叫被称为 虚函数调用。 [

【讨论】:

  • 感谢您的回复。您是否介意再次检查我的问题并更详细地详细说明每个问题? (参考 C++ 标准,范围运算符,...)
  • 1) 在 C::f() 内部调用 B::f() 基本同:B b; b.f();对? 2)你能指出标准中的一个部分吗?
  • 许多知名框架实际上生成的代码类似于 OP 的示例。这是明确定义的标准行为和实用性。基本上在任何 OO GUI 框架中,用户代码都应该在覆盖内调用覆盖的一个基本重载。
猜你喜欢
  • 2012-03-17
  • 1970-01-01
  • 1970-01-01
  • 2012-08-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-06-29
  • 1970-01-01
相关资源
最近更新 更多