【问题标题】:virtual function hierarchy in c++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 默认被认为是虚拟的?

【问题讨论】:

标签: c++ virtual


【解决方案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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2022-11-17
        • 1970-01-01
        • 1970-01-01
        • 2022-01-12
        • 2014-11-12
        • 1970-01-01
        • 2013-01-05
        相关资源
        最近更新 更多