Jed Schaaf Jed Schaaf - 3 months ago 12
C++ Question

What are the differences between overriding virtual functions and hiding non-virtual functions?

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();
}

dyp dyp
Answer

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 class Base and in a class Derived, derived directly or indirectly from Base, a member function vf with the same name, parameter-type-list, cv-qualification, and ref-qualifier (or absence of same) as Base::vf is declared, then Derived::vf is also virtual (whether or not it is so declared) and it overrides Base::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 object S is a final overrider unless the most derived class of which S is a base class subobject (if any) declares or inherits another member function that overrides vf.

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)`
};