C++ Function Overriding


Introduction

Function overriding in C++ is a feature of object-oriented programming that allows a derived class to provide a specific implementation of a function that is already defined in its base class. This mechanism is used to achieve polymorphism, which allows the program to decide at runtime which version of the function to call based on the object type (whether it’s a base class or derived class object).

In function overriding, the signature of the base class function and the overridden function in the derived class must be the same. The overridden function is called when an object of the derived class invokes that function, even when the function is invoked via a pointer or reference of the base class type.


1. Function Overriding Syntax

To override a function in C++, the function in the derived class must:

  • Have the same name as the function in the base class.
  • Have the same return type.
  • Have the same parameter list.

Additionally, the virtual keyword is typically used in the base class function declaration to allow for runtime polymorphism.

Syntax:

class BaseClass {
public:
    virtual void functionName() {
        // Base class implementation
    }
};

class DerivedClass : public BaseClass {
public:
    void functionName() override {
        // Derived class implementation
    }
};
  • The virtual keyword indicates that the function can be overridden in derived classes.
  • The override keyword (optional, but recommended) is used to indicate that the function is meant to override a base class function.

2. Example of Function Overriding

Let’s understand function overriding through an example.

#include <iostream>
using namespace std;

// Base class
class Animal {
public:
    virtual void sound() {
        cout << "Animal makes a sound!" << endl;
    }
};

// Derived class
class Dog : public Animal {
public:
    void sound() override {
        cout << "Dog barks!" << endl;
    }
};

// Another derived class
class Cat : public Animal {
public:
    void sound() override {
        cout << "Cat meows!" << endl;
    }
};

int main() {
    Animal* animal1 = new Dog();
    Animal* animal2 = new Cat();

    animal1->sound();  // Output: Dog barks!
    animal2->sound();  // Output: Cat meows!

    delete animal1;
    delete animal2;

    return 0;
}

Explanation:

  • The sound() function is defined in the base class Animal and is marked as virtual to allow overriding.
  • Both the Dog and Cat classes override the sound() function to provide their own implementations.
  • In the main() function, we create objects of Dog and Cat but use pointers of type Animal* to call the sound() function. This demonstrates runtime polymorphism, where the correct version of the function (based on the actual object type) is called at runtime.

3. Key Points About Function Overriding

  • Virtual Function: In C++, for a function to be overridden in a derived class, it should be declared as virtual in the base class. This enables dynamic dispatch, where the function call is resolved at runtime based on the object type.

  • Overridden Function: The function in the derived class must have the same signature as the function in the base class. This includes the function name, return type, and parameters.

  • Access Modifiers: The access modifiers (like public, protected, or private) should match or be compatible between the base class and the derived class for the overridden function. Typically, functions in the derived class are made public so that they can be accessed outside the class.

  • Virtual Table (vtable): When using function overriding, C++ uses a virtual table to ensure that the correct function is called based on the object's actual type at runtime. This mechanism is central to runtime polymorphism.

  • Override Keyword: The override keyword is not mandatory, but it is a good practice to use it in the derived class to avoid errors, such as when the function signature in the derived class does not match the base class.

  • Destructors and Virtual Functions: If a class has a virtual function, its destructor should also be virtual to ensure proper cleanup when an object is deleted through a base class pointer.


4. Example of Function Overriding with Destructor

In the example below, we will show how to use a virtual destructor to ensure proper cleanup when objects are deleted using base class pointers.

#include <iostream>
using namespace std;

// Base class
class Base {
public:
    virtual ~Base() {  // Virtual destructor in base class
        cout << "Base class destructor called!" << endl;
    }
    virtual void show() {
        cout << "Base class show function" << endl;
    }
};

// Derived class
class Derived : public Base {
public:
    ~Derived() override {  // Overriding destructor in derived class
        cout << "Derived class destructor called!" << endl;
    }
    void show() override {
        cout << "Derived class show function" << endl;
    }
};

int main() {
    Base* basePtr = new Derived();
    basePtr->show();  // Output: Derived class show function
    delete basePtr;   // Calls Derived class destructor first, then Base class destructor

    return 0;
}

Explanation:

  • The Base class has a virtual destructor to ensure that the destructor of the derived class (Derived) is called when deleting a Derived object using a Base pointer.
  • When delete basePtr; is called, the destructor of the Derived class is invoked first, followed by the destructor of the Base class. This ensures proper resource cleanup in the derived class.

5. When to Use Function Overriding

You should use function overriding when:

  • You want to provide a specific implementation of a method that is already provided by a base class.
  • You need to take advantage of polymorphism so that the correct function is called based on the actual type of the object (even when using base class pointers or references).
  • You want to extend or modify the behavior of a base class function in a derived class without modifying the base class itself.