Member Function Selection and Overriding

Member Function Invocation

When a member function is called with a derived class object, the compiler looks for that member in the derived class. If not found, it traverses the inheritance chain, checking each parent class for the existence of the member. The compiler utilizes the most-derived version of the function it finds.

Consider the following example:

#include <iostream>

class Base {
protected:
    int m_value;

public:
    Base(int value) : m_value(value) {}

    void identify() const { std::cout << "I am a Base\n"; }
};

class Derived : public Base {
public:
    Derived(int value) : Base(value) {}
};

int main() {
    Base base{5};
    base.identify();

    Derived derived{7};
    derived.identify();

    return 0;
}

// Output
I am a Base
I am a Base

When derived.identify() is called, the compiler searches for identify() in the Derived class, but since it is not defined there, it looks in the inherited class (Base). As Base has the identify() function, it uses that one

Redefining Behaviors

If we define Derived::identify() in the Derived class, it will be used instead. This allows us to make functions work differently in the derived class by redefining them.

class Derived : public Base {
public:
    Derived(int value) : Base(value) {}

    void identify() const { std::cout << "I am a Derived\n"; }
};

Now, calling derived.identify() results in:

I am a Base
I am a Derived

The derived function does not inherit the access specifier of the base function; it uses its own access specifier.

class Base {
private:
    void print() const {
        std::cout << "Base";
    }
};

class Derived : public Base {
public:
    void print() const {
        std::cout << "Derived ";
    }
};

Here, Derived::print() is public, although Base::print() is private

Adding to Existing Functionality

Sometimes, rather than completely replacing a base class function, we want to add additional functionality. In the example, Derived::identify() hides Base::identify(). To call the base version of the function and add extra functionality, use the scope qualifier:

class Derived : public Base {
public:
    Derived(int value) : Base(value) {}

    void identify() const {
        Base::identify(); // Call Base::identify() first
        std::cout << "I am a Derived\n"; // Then identify ourselves
    }
};

Now, calling derived.identify() produces:

I am a Base
I am a Derived