使用多态指针调用函数

calling to functions with polymorphic pointers

本文关键字:调用 函数 指针 多态      更新时间:2023-10-16
#include <iostream>
class A{};
class AA:public A{};
struct X {
    void f(A *) {
        std::cout << "X::f(A*)n";
    }
};
struct Y:public X {
    void f(A *) {
        std::cout << "Y::f(A*)n";
    }
};
int main() {
    Y *y = new Y();
    X *x = new Y();
    const X *cx = new Y();
    y->X::f(new AA);
    x->f(new AA);
}

打印:

X::f(A*(

X::f(A*(

我不明白为什么y->X::f(new AA)x->f(new AA)不会引发编译错误。我知道在这两种情况下都调用了X::f(A *)。但是编译器选择使用此方法的原理是什么?多态性函数过载?有什么经验规则吗?

你的问题意味着你期望在 X 中声明 f 是虚拟时你会得到的行为。 在某些语言中,这是唯一可能的行为。 C++你有一个选择。

正如您声明的 f 所示,它使用编译时类型,因此x-f(new AA)调用X::f因为x是一个X*

如果您在 X 中声明了 f virtual,则x-f(new AA)将调用 Y::f,因为 x 指向 Y

对于问题的另一个方面(为什么为 Y 类型的对象调用 X::f 不涉及错误(,X 是 Y 的基类,因此应该有一个 X 类型的有效对象埋在 Y 类型的每个对象中,并且该 X 对象应该可用于任何 X 方法。

在看到上面的评论之前,我错过了您问题的第三个方面,即当您将AA*传递给需要A*的方法并且AAA的基类时,编译器会推断出需要从AA*A*的隐式强制转换。 该推导在很大程度上与 X::f 和 Y::f 之间的编译时或运行时选择无关

如果你这样做,你的代码目前是这样的:

y->X::f(new AA);
y->f(new AA);
x->f(new AA);

你会得到这个:

X::f(A*)
Y::f(A*)
X::f(A*)

但是,如果您将X::f声明为虚拟,则会得到以下内容:

X::f(A*)
Y::f(A*)
Y::f(A*)

此外,由于 AAA 的子类,因此可以将指针传递到AA需要指向A的指针的位置。

如果你想

x->f(new AA)调用Y::f(),那么你需要将X::f()声明为virtual.

如果X::f()没有声明为 virtual ,则(因为x是一个X指针(x->f(new AA)调用X::f()

请参阅C++标准第 10.3 节"虚拟功能"(N3242(:

  1. 虚函数支持动态绑定和面向对象编程。声明或 继承的虚函数称为多态类。

  2. 如果虚拟成员函数vf在类Base和类Derived中声明,则直接或间接派生 从 Base 开始,一个成员函数vf与声明的名称、参数类型列表、CV 限定符和 refqualifier(或不存在相同(Base::vf,则Derived::vf也被virtual(无论是否如此声明(并覆盖Base::vf