调用姐姐class的一个函数C++

Calling a function of sister class C++

考虑以下代码:

#include <iostream>

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

class B : virtual public A
{
public:
    virtual void f()
    {
        g();
    }
};

class C : virtual public A
{
public:
    virtual void g()
    {
        std::cout << "C::g" << std::endl;
    }
};

class D : public C, public B
{
};

int main()
{
    B* b = new D;
    b->f();
}

以下程序的输出是C::g

编译器如何调用classB的姐姐class的函数??

这是 virtual 的行为:B 通过 f 调用 gg 在运行时解析(如 f) .因此,在运行时,Dg 唯一可用的覆盖是 C

中实现的覆盖

g 像所有虚函数一样在运行时解析。由于 D 的定义方式,它被解析为任何 C 实现。

如果您不想要这种行为,您应该调用 g 的非虚拟实现(您也可以从虚拟函数委托给该函数),或者显式调用 B 的实现使用 B::g().

虽然如果你这样做,你的设计将比它可能需要的复杂得多,所以尝试找到一个不依赖于所有这些技巧的解决方案。

虚拟函数调用在运行时通过引用实例的 VTable 来解析。任何虚拟 class 都存在不同的 VTable(因此在上面每个 ABCD 都有一个 VTable).每个运行时实例都有一个指向这些表之一的指针,由其动态类型决定。

VTable 列出了 class 中的每个虚函数,将其映射到应在运行时调用的实际函数。对于正常继承,它们按声明顺序列出,因此基 class 可以使用派生 class 的 VTable 来解析它声明的虚函数(因为派生 class, 按声明顺序列出函数,将首先列出所有基 class 的函数,其顺序与基 class 自己的 VTable 的顺序完全相同。对于虚拟继承(如上所述),这稍微复杂一些,但本质上派生 class 中的基 class 仍然有自己的 VTable 指针指向派生的相关部分VTable

实际上这意味着您的 D class 有一个 gVTable 条目指向 C 的实现。即使通过 B 静态类型访问,它仍然会引用相同的 VTable 来解析 g.

N3337 10.3/9

[ Note: The interpretation of the call of a virtual function depends on the type of the object for which it is called (the dynamic type), whereas the interpretation of a call of a non-virtual member function depends only on the type of the pointer or reference denoting that object (the static type) (5.2.2). — end note ]

动态类型是指针真正指向的类型,而不是声明为指向类型的类型。

因此:

D d;
d.g(); //this results in C::g as expected

等同于:

B* b = new D;
b->g();

并且因为在您对 g()B::f 调用中( 隐含地 )调用 this 指针,其 动态类型 D,调用解析为 D::f,即 C::f

如果你仔细观察,它与上面代码中显示的(完全)行为相同,只是 b 现在是 隐式 this 代替。

这就是虚函数的全部意义。