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

时间:2015-02-03 19:39:53

标签: c++ virtual

我对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::fC覆盖B::f并具有相同的签名。

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

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

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

4 个答案:

答案 0 :(得分:3)

A::fB::f是两个不同的功能,即使它们具有相同的名称;由于B::f未声明为virtual,因此只有A::f是虚拟的 - 并且没有人覆盖它。

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

答案 1 :(得分:2)

  

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)存在。

答案 2 :(得分:1)

  

是否有汇总所有可能情况的流程图?

完全一般,你需要知道C ++中的整套名称查找规则,遗憾的是这很复杂。

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

  

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

...

  

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

...

  

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

     
      
  • 都是指向类的指针,两者都是对类的左值引用,或者两者都是对它们的右值引用   类
  •   
  • 返回类型B::f中的类与返回类型D::f中的类相同,或者是   返回类型D::f
  • 中类的明确且可访问的直接或间接基类   
  • 指针或引用在D::f的返回类型中具有相同的cv-qualification和类类型   与B::f的返回类型中的类类型具有相同的cv资格或更少的cv资格。
  •   

由于B::fA::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()始终在派生类中调用B::f而不调用任何名为f的函数。

答案 3 :(得分:0)

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

  

如果在类vf和类Base中声明虚拟成员函数Derived,直接或间接地从Base派生,则成员函数{{1声明具有相同名称, parameter-type-list (8.3.5),cv-qualification和ref-qualifier(或缺少相同),{}} { {1}}也是虚拟的(无论是否如此声明)并且覆盖 vf

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