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();
}
What is function hiding?
... 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
}
};
What is function overriding?
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(); // dynamic type of `*p` is `Derived`
Base& b = *p;
b . vf(); // dynamic type of `b` is `Derived`
What is the difference between overriding and hiding?
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)
How do they relate to function overloads?
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.
A much easier example that differs b/w all of them.
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
}
We'll start with the easy ones.
p1
is a Parent
pointer, so it will always call Parent
's member functions.
cp
is a pointer to Child
, so it will always call Child
's member functions.
Now the more difficult one. p2
is a Parent
pointer, but it is pointing to an object of type Child
, so it will call Child
's functions whenever the matching Parent
function is virtual or the function only exists within Child
and not in Parent
. In other words, Child
hides Parent::doA()
with its own doA()
, but it overrides Parent::doB()
. Function hiding is sometimes considered a form of function overloading, because a function with the same name is given a different implementation. Because the hiding function is in a different class than the hidden function, it does have a different signature, which makes it clear which to use.
The output for testStuff()
will be
doA in Parent
doA in Parent
doA in Child
doB in Parent
doB in Child
doB in Child
In any case, Parent::doA()
and Parent::doB()
can be called within Child
using name resolution, regardless of the function's "virtual-ness". The function
void Child::doX() {
doA();
doB();
Parent::doA();
Parent::doB();
cout << "doX in Child" << endl;
}
demonstrates this when called by cp->doX()
by outputting
doA in Child
doB in Child
doA in Parent
doB in Parent
doX in Child
Additionally, cp->Parent::doA()
will call Parent
's version of doA()
.
p2
cannot refer to doX()
because it is a Parent*
, and Parent
doesn't know about anything in Child
. However, p2
can be casted to a Child*
, since it was initialized as one, and then it can be used to call doX()
.
The example code you're writen in the question essentially gives the answer when you run it.
Calling a non-virtual function will use the function from the same class as the pointer type, regardless of whether the object was actually created as some other derived type. Whereas calling a virtual function will use the function from the original allocated object type, regardless of what kind of pointer you're using.
So your program's output in this case will be:
doA in Parent
doA in Parent
doA in Child
doB in Parent
doB in Child
doB in Child
来源:https://stackoverflow.com/questions/19736281/what-are-the-differences-between-overriding-virtual-functions-and-hiding-non-vir