调用姐姐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
调用 g
但 g
在运行时解析(如 f
) .因此,在运行时,D
的 g
唯一可用的覆盖是 C
中实现的覆盖
g
像所有虚函数一样在运行时解析。由于 D
的定义方式,它被解析为任何 C 实现。
如果您不想要这种行为,您应该调用 g
的非虚拟实现(您也可以从虚拟函数委托给该函数),或者显式调用 B
的实现使用 B::g()
.
虽然如果你这样做,你的设计将比它可能需要的复杂得多,所以尝试找到一个不依赖于所有这些技巧的解决方案。
虚拟函数调用在运行时通过引用实例的 VTable
来解析。任何虚拟 class 都存在不同的 VTable
(因此在上面每个 A
、B
、C
和 D
都有一个 VTable
).每个运行时实例都有一个指向这些表之一的指针,由其动态类型决定。
VTable
列出了 class 中的每个虚函数,将其映射到应在运行时调用的实际函数。对于正常继承,它们按声明顺序列出,因此基 class 可以使用派生 class 的 VTable
来解析它声明的虚函数(因为派生 class, 按声明顺序列出函数,将首先列出所有基 class 的函数,其顺序与基 class 自己的 VTable
的顺序完全相同。对于虚拟继承(如上所述),这稍微复杂一些,但本质上派生 class 中的基 class 仍然有自己的 VTable
指针指向派生的相关部分VTable
。
实际上这意味着您的 D
class 有一个 g
的 VTable
条目指向 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
代替。
这就是虚函数的全部意义。
考虑以下代码:
#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
调用 g
但 g
在运行时解析(如 f
) .因此,在运行时,D
的 g
唯一可用的覆盖是 C
g
像所有虚函数一样在运行时解析。由于 D
的定义方式,它被解析为任何 C 实现。
如果您不想要这种行为,您应该调用 g
的非虚拟实现(您也可以从虚拟函数委托给该函数),或者显式调用 B
的实现使用 B::g()
.
虽然如果你这样做,你的设计将比它可能需要的复杂得多,所以尝试找到一个不依赖于所有这些技巧的解决方案。
虚拟函数调用在运行时通过引用实例的 VTable
来解析。任何虚拟 class 都存在不同的 VTable
(因此在上面每个 A
、B
、C
和 D
都有一个 VTable
).每个运行时实例都有一个指向这些表之一的指针,由其动态类型决定。
VTable
列出了 class 中的每个虚函数,将其映射到应在运行时调用的实际函数。对于正常继承,它们按声明顺序列出,因此基 class 可以使用派生 class 的 VTable
来解析它声明的虚函数(因为派生 class, 按声明顺序列出函数,将首先列出所有基 class 的函数,其顺序与基 class 自己的 VTable
的顺序完全相同。对于虚拟继承(如上所述),这稍微复杂一些,但本质上派生 class 中的基 class 仍然有自己的 VTable
指针指向派生的相关部分VTable
。
实际上这意味着您的 D
class 有一个 g
的 VTable
条目指向 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
代替。
这就是虚函数的全部意义。