Given the following code fragment, what are the differences in the function calls? What is function hiding? What is function overriding? How do they relate to function overloads? What is the difference between the two? I couldn't find a good description of these in one place, so I'm asking here so I can consolidate the information.
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(); }
The virtual keyword can be used when declaring overriding functions in a derived class, but it is unnecessary; overrides of virtual functions are always virtual. Virtual functions in a base class must be defined unless they are declared using the pure-specifier.
With the virtual keyword on the base class method and the override keyword on the method in the derived class, both methods are said to be virtual. Methods that don't have either the virtual or override keywords, or that have the new keyword, are said to be non-virtual.
So for example if you created an instance of your derived class but called your 'Display' method via a pointer to the base class, the base's method will be called, whereas for 'vDisplay' the derived method would be called. Base *b = &ba; b->Display(); b->vDisplay(); b = &de; b->Display(); b->vDisplay();
Non-virtual member functions are resolved statically. That is, the member function is selected statically (at compile-time) based on the type of the pointer (or reference) to the object. In contrast, virtual member functions are resolved dynamically (at run-time).
... is a form of name hiding. A simple example:
void foo(int); namespace X { void foo(); void bar() { foo(42); // will not find `::foo` // because `X::foo` hides it } }
This also applies to the name lookup in a base class:
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 } };
This is linked to the concept of virtual functions. [class.virtual]/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
.
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) };
The final overrider becomes relevant when calling a virtual function: [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
.
I.e. if you have an object of type S
, the final overrider is the first overrider you see when traversing the class hierarchy of S
back to its base classes. The important point is that the dynamic type of the function-call expression is used in order to determine the final overrider:
Base* p = new Derived; p -> vf(42); // dynamic type of `*p` is `Derived` Base& b = *p; b . vf(42); // dynamic type of `b` is `Derived`
Essentially, the functions in the base class are always hidden by functions of the same name in a derived class; no matter if the function in the derived class overrides a base class' virtual function or not:
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)` };
To find a function name, the static type of an expression is used:
Derived d; d.vf(42); // `vf` is found as `Derived::vf()`, this call is ill-formed // (too many arguments)
As "function hiding" is a form of name hiding, all overloads are affected if the name of a function is hidden:
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)` };
For function overriding, only the function in the base class with the same arguments will be overriden; you can of course overload a virtual function:
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)` };
The difference between calling a virtual member function and calling a non-virtual member function is that, by definition, in the former case the target function is chosen in accordance with the dynamic type of the object expression used in the call, while in the latter case the static type is used.
That's all there is to it. Your example clearly illustrates this difference by p2->doA()
and p2->doB()
calls. Static type of *p2
expression is Parent
, while dynamic type of the same expression is Child
. This is why p2->doA()
calls Parent::doA
and p2->doB()
calls Child::doB
.
In contexts in which that difference matters, name hiding does not come into the picture at all.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With