What are the differences between overriding virtual functions and hiding non-virtual functions?
给定以下代码片段,函数调用有什么区别?什么是函数隐藏?什么是函数重写?它们如何与函数重载相关?这两者有什么区别?我在一个地方找不到这些的好描述,所以我在这里询问,以便整合信息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | class Parent { public: void doA() { cout <<"doA in Parent" << endl; } virtual void doB() { cout <<"doB in Parent" << endl; } }; class Child : public Parent { public: void doA() { cout <<"doA in Child" << endl; } void doB() { cout <<"doB in Child" << endl; } }; Parent* p1 = new Parent(); Parent* p2 = new Child(); Child* cp = new Child(); void testStuff() { p1->doA(); p2->doA(); cp->doA(); p1->doB(); p2->doB(); cp->doB(); } |
什么是函数隐藏?
…是名称隐藏的一种形式。一个简单的例子:
1 2 3 4 5 6 7 8 9 10 11 | void foo(int); namespace X { void foo(); void bar() { foo(42); // will not find `::foo` // because `X::foo` hides it } } |
这也适用于基类中的名称查找:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | class Base { public: void foo(int); }; class Derived : public Base { public: void foo(); void bar() { foo(42); // will not find `Base::foo` // because `Derived::foo` hides it } }; |
什么是函数重写?
这与虚拟函数的概念有关。[类.虚拟]/2
If a virtual member function
vf is declared in a classBase and in a classDerived , derived directly or indirectly fromBase , a member functionvf with the same name, parameter-type-list, cv-qualification, and ref-qualifier (or absence of same) asBase::vf is declared, thenDerived::vf is also virtual (whether or not it is so declared) and it overridesBase::vf .
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | class Base { private: virtual void vf(int) const &&; virtual void vf2(int); virtual Base* vf3(int); }; class Derived : public Base { public: // accessibility doesn't matter! void vf(int) const &&; // overrides `Base::vf(int) const &&` void vf2(/*int*/); // does NOT override `Base::vf2` Derived* vf3(int); // DOES override `Base::vf3` (covariant return type) }; |
调用虚函数[class.virtual]/2时,最终的重写器变得相关。
A virtual member function
C::vf of a class objectS is a final overrider unless the most derived class of whichS is a base class subobject (if any) declares or inherits another member function that overridesvf .
也就是说,如果您有一个
1 2 3 4 5 | Base* p = new Derived; p -> vf(); // dynamic type of `*p` is `Derived` Base& b = *p; b . vf(); // dynamic type of `b` is `Derived` |
覆盖和隐藏有什么区别?
基本上,基类中的函数总是被派生类中同名的函数隐藏;无论派生类中的函数是否重写基类的虚函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 | class Base { private: virtual void vf(int); virtual void vf2(int); }; class Derived : public Base { public: void vf(); // doesn't override, but hides `Base::vf(int)` void vf2(int); // overrides and hides `Base::vf2(int)` }; |
要查找函数名,将使用表达式的静态类型:
1 2 3 | Derived d; d.vf(42); // `vf` is found as `Derived::vf()`, this call is ill-formed // (too many arguments) |
它们如何与函数重载相关?
由于"函数隐藏"是名称隐藏的一种形式,如果函数名被隐藏,则所有重载都会受到影响:
1 2 3 4 5 6 7 8 9 10 11 12 | class Base { private: virtual void vf(int); virtual void vf(double); }; class Derived : public Base { public: void vf(); // hides `Base::vf(int)` and `Base::vf(double)` }; |
对于函数重写,只重写具有相同参数的基类中的函数;当然,可以重载虚函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | class Base { private: virtual void vf(int); virtual void vf(double); void vf(char); // will be hidden by overrides in a derived class }; class Derived : public Base { public: void vf(int); // overrides `Base::vf(int)` void vf(double); // overrides `Base::vf(double)` }; |
调用虚成员函数和调用非虚成员函数的区别在于,根据定义,前者根据调用中使用的对象表达式的动态类型选择目标函数,后者使用静态类型。
就这些了。您的示例通过
在这种差异很重要的情况下,名称隐藏根本不会出现在图片中。
一个更简单的例子,它不同于所有的b/w。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | class Base { public: virtual int fcn(); }; class D1 : public Base { public: // D1 inherits the definition of Base::fcn() int fcn(int); // parameter list differs from fcn in Base virtual void f2(); // new virtual function that does not exist in Base }; class D2 : public D1 { public: int fcn(int); // nonvirtual function hides D1::fcn(int) int fcn(); // overrides virtual fcn from Base void f2(); // overrides virtual f2 from D1 } |
在问题中编写的示例代码在运行时基本上给出了答案。
调用非虚拟函数将使用与指针类型相同的类中的函数,而不管对象是否实际创建为其他派生类型。然而,无论您使用的是哪种指针,调用虚拟函数都将使用原始分配对象类型的函数。
因此,在这种情况下,您的程序的输出将是:
1 2 3 4 5 6 | doA in Parent doA in Parent doA in Child doB in Parent doB in Child doB in Child |
我们从简单的开始。
现在更难了。
1 2 3 4 5 6 | doA in Parent doA in Parent doA in Child doB in Parent doB in Child doB in Child |
在任何情况下,可以使用名称解析在
1 2 3 4 5 6 7 | void Child::doX() { doA(); doB(); Parent::doA(); Parent::doB(); cout <<"doX in Child" << endl; } |
当
1 2 3 4 5 | doA in Child doB in Child doA in Parent doB in Parent doX in Child |
另外,