C++ 中的虚函数层次结构

virtual function hierarchy in c++

本文关键字:函数 层次结构 C++      更新时间:2023-10-16

我对 c++ 中的虚函数主题有点困惑。是否有总结所有可能案例的流程图?

例如:

    class A {
       public:
          virtual void f(const int i) { cout << "A::f" << endl; }
    };
    class B : public A {
       public:
          // Hide A's f with new implementations
          void f() { cout << "B::f" << endl; }
    };
    class C : public B {
       public:
          void f() { cout << "C::f" << endl; }
    };
class D : public B { public:
    void f(const int i) { cout << "D::f" << endl; }
};

    void main() {
       D d;
       C c;
       B* pb = &c;
       pb->f();
       A* pa = &d;
       A* paa = &c;
       pa->f(1);
       paa->f(1); //in here C::f would be invoked?
    }

在这种情况下,B 隐藏A::f ,并且C具有相同签名的B::f的覆盖。

pb->f()会调用C::f吗?pa->f(1)会调用A::f吗?

我要求它知道B::f是否仍然被认为是虚拟的,因此它的衍生类可以覆盖它,尽管它隐藏了A::f

以及 如果 C::f 默认被视为虚拟?

A::fB::f是两个不同的函数,即使它们具有相同的名称;由于B::f没有被声明为virtual所以只有A::f是虚拟的 - 没有人覆盖它。

pb->f()使用指针的静态类型(即B*)来确定要调用的函数;这将是B::f

pb->f()会调用C::f吗?

不,不会。

我要求它知道B::f是否仍然被认为是virtual,以便它的衍生类可以覆盖它,尽管它隐藏了A::f

B::f()不是virtual成员函数。

要使B::f()成为虚拟成员函数,您必须使用:

class B : public A {
   public:
      // Hide A's f with new implementations
      virtual void f() { cout << "B::f" << endl; }
};

更新

您用以下附加问题更新了帖子:

pa->f(1)会调用A::f吗?

是的,会的。通过pa看,B::f()C::f()都不存在,只有A::f(int)存在。

是否有总结所有可能案例的流程图?

一般来说,您需要了解C++中的整套名称查找规则,不幸的是,这非常复杂。

假设您只想知道什么覆盖了什么,您可以在标准的几个段落中找到所有详细信息。

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

即使析构

函数不是继承的,派生类中的析构函数也会重写基类析构函数 声明虚拟;见12.4和12.5。

重写函数的返回类型

应与重写函数的返回类型相同 函数或与函数类的协变。如果函数D::f覆盖函数B::f , 如果函数的返回类型满足以下条件,则它们是协变的:

    两者都是指向类的指针,都是对
  • 类的左值引用,或者都是对 类
  • 返回类型B::f中的类与返回类型D::f中的类相同,或者是一个 返回类型中类的明确且可访问的直接或间接基类D::f
  • 指针或引用具有相同的 CV 限定条件和返回类型中的类类型D::f 与返回类型"B::f"中的类类型具有相同或更少的简历资格。

因为B::f没有与A::f相同的参数类型,所以B::f不会覆盖A::f,并且出于同样的原因,C::f不会覆盖A::f。由于B::f没有声明virtual并且它不会覆盖A::fB::f不是虚拟的。 C::f不会覆盖B::f B::f

因为不是虚拟的。

由于B::f不是虚拟的,因此pb->f()始终调用派生类中名为f的任何函数B::f而不是任何函数。

B::f 不会覆盖A::f因为它们没有相同的参数类型列表,因此它也不是虚拟的,[class.virtual]/p2:

如果一个虚成员函数vf是在类Base和类Derived中声明的,直接或间接派生自Base,则成员函数vf具有与声明Base::vf相同的名称、参数类型列表 (8.3.5)、cv 限定符和 ref 限定符(或没有相同),则Derived::vf也是虚拟的(无论它是否如此声明),并且它覆盖Base::vf

因此,派生类方法无论是否具有virtual说明符,都可以隐式虚拟,前提是它与基类方法的某些属性匹配。在这种情况下,B::f不是虚拟的,因此pb->f()使用指针的静态类型,B* ,并调用B::f而不是C::f