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

Posted

技术标签:

【中文标题】C++中的虚函数层次结构【英文标题】:virtual function hierarchy in c++ 【发布时间】:2015-02-03 19:39:53 【问题描述】:

我对 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-&gt;f() 会调用C::f 吗? pa-&gt;f(1) 会调用A::f 吗?

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

如果 C::f 默认被认为是虚拟的?

【问题讨论】:

“如果您提供 virtual 关键字,pb-&gt;f() 会调用 C::f 吗?” No. 和 yes。 你的意思是一旦你隐藏了基础虚拟函数,隐藏函数默认不是虚拟的? Virtual function calling using dereference object 的可能副本 @Day_Dreamer 您可能还想在这里阅读:***.com/questions/18198314/override-keyword-in-c 【参考方案1】:

A::fB::f 是两个不同的函数,尽管它们具有相同的名称;因为 B::f 没有被声明为 virtual 只有 A::f 是虚拟的 - 并且没有人覆盖它。

pb-&gt;f()使用指针的静态类型B*来判断调用哪个函数;那将是B::f

【讨论】:

【参考方案2】:

pb-&gt;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-&gt;f(1) 会调用A::f 吗?

是的,会的。看透pa,既不存在B::f()也不存在C::f(),只存在A::f(int)

【讨论】:

如果我用相同的签名声明 B::f 它将默认是虚拟的,即使没有明确的“虚拟”。所以你暗示当 B::f 隐藏具有不同签名的 A::f 时默认的“虚拟”被删除? @Day_Dreamer,部分正确。默认情况下,函数不是虚拟的。由于B::f()A::f(int) 无关,所以默认不是虚拟的。因此,删除“虚拟”不是正确的思考方式。 “虚拟”不是一开始就存在的。 @Day_Dreamer:没错,一个函数只有在重写虚函数时才是隐式虚函数。【参考方案3】:

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

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

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

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

...

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

...

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

都是指向类的指针,都是指向类的左值引用,或者都是指向类的右值引用 课程 B::f返回类型中的类与D::f返回类型中的类相同,或者是 D::f返回类型中类的明确且可访问的直接或间接基类 指针或引用都具有相同的 cv 限定和 D::f 返回类型中的类类型 与B::f的返回类型中的类类型具有相同或更少的cv-qualification。

因为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-&gt;f() 总是调用B::f,而不是派生类中任何名为f 的函数。

【讨论】:

【参考方案4】:

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

如果在类Base和类Derived中声明了一个虚成员函数vf,直接或间接派生自Base,则一个同名的成员函数vf parameter-type-list (8.3.5)、cv-qualification 和 ref-qualifier(或不存在相同)作为 Base::vf 声明,则 Derived::vf 也是虚拟的(无论是否如此声明)并且它覆盖 Base::vf

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

【讨论】:

以上是关于C++中的虚函数层次结构的主要内容,如果未能解决你的问题,请参考以下文章

c++中的虚函数

C++中的虚函数以及虚函数表

9-4:C++多态之单继承和多继承中的虚函数表

c++中的虚函数有啥作用?

为啥“基类对象”不能调用它自己的虚函数? C++

关于C++中虚函数表的几点总结