Function Overriding in C++: Comprehensive Guide with Real-Life Examples

Updated on October 22, 2024

Article Outline

Have you ever had a situation where the behaviour of a function in a base class simply wasn’t what you needed it to be in a derived class? That’s where function overriding comes in handy in C++.

 

Imagine you work on some project having functions within the base class. Now, you need to adjust them to suit your derived class context, but you don’t want to change the base class itself. Well, that is what we’d be solving here with function overriding.

 

By the end of this article, function overriding in C++ will not only be a thing you’ll understand, but you’ll be ready to use it in your projects.

function overriding in C++

What is Function Overriding in C++?

Function overriding, in simple terms, gives us the choice of overriding how a function will behave in a derived class without having to tamper with the base class. It is one of the key players in polymorphism, which simply means that, under many constellations, a function can behave differently based on who makes the call for it.

 

But here’s the catch: getting function overriding right can’t always be simple. Most of the time, missing virtual keywords or a mistaken set of signatures makes things confusing and introduces bugs that could be really tough to catch. So, we are going to break it down in a super duper, simple way with code examples, along with real-life analogies.

overriding

 

Also Read: Class and Objects in C++

*Image
Get curriculum highlights, career paths, industry insights and accelerate your technology journey.
Download brochure

Why is Inheritance Important for Function Overriding?

Before proceeding further with function overriding, let’s just take a step back here and talk a little bit about inheritance.

 

Why does it matter?

 

Because without inheritance, function overriding would not have existed either.

 

Imagine inheritance, which is like passing a tradition down to your family. A base class passes down traits (or functions) to a derived class. The derived class then decides whether it wants to keep those traits as is or change them. Overriding is when we decide to change one of those inherited traits.

 

Suppose we have a base class called Vehicle. It has a method named start(). The way all vehicles start is pretty much the same: turn the key, and the engine comes to life. But what if I have an electric car? I don’t turn a key; I push a button.

 

Instead of fixing all the vehicle code, we are going to inherit from the Vehicle class and then override the start() method in the derived ElectricCar class.

 

Otherwise, you would have to start from scratch every time there was a change, which would amount to more code, more bugs, and more headaches. Overriding saves you from that.

Detailed Explanation of How Function Overriding Works in C++

Now, let’s get to the good stuff: how function overriding works in C++.

 

Here’s the bottom line: overridden functions allow a derived class to implement a version of a function that already exists in the base class. What’s important to keep in mind? The function signature must be identical. That is to say:

  • Same name
  • Same parameters
  • Same return type

We need to mark the base class function with a virtual keyword. If not, then that function will not be overridden; it’ll be hidden, and the base class function will be executed in this case.

 

Now, let’s consider an example for this so that you really get the point across:

#include <iostream> using namespace std; class Vehicle { public: // Base class function with virtual keyword virtual void start() { cout << "Vehicle is starting..." << endl; } }; class ElectricCar : public Vehicle { public: // Overriding the start() function in the derived class void start() override { cout << "Electric Car is starting with a button..." << endl; } }; int main() { // Creating objects of both types Vehicle myVehicle; ElectricCar myTesla; // Calling the start function myVehicle.start();   // Outputs: Vehicle is starting... myTesla.start();     // Outputs: Electric Car is starting with a button... }

In this code:

  • We have a base class Vehicle with a start() function.
  • The ElectricCar class inherits from Vehicle, but we override start() to reflect how an electric car starts.
  • When we call start() using the ElectricCar object, it runs the overridden version.
  • The virtual keyword makes it work. Without it, the base class function would be called, which is not what we want when overriding.

Step-by-step Syntax and Implementation of Function Overriding in C++

Let us understand the syntax step-by-step so that there will be no confusion.

Declaring the base class:

  • Use the virtual keyword for the function that may be overridden.
  • Example: virtual void start() {}

Inherit the base class in the derived class:

  • Use the public keyword to ensure that the base class functions are accessible.
  • Example: class ElectricCar : public Vehicle {}

Override the base class function in the derived class:

  • Use the override keyword to make sure the function is properly overridden.
  • Example: void start() override {}

class function in the derived class

Finally, let’s add something: user input. We will modify the above example so that it takes user input to choose the type of vehicle and outputs the appropriate start method.

#include <iostream> #include <string> using namespace std; class Vehicle { public: virtual void start() { cout << "Vehicle is starting..." << endl; } }; class ElectricCar : public Vehicle { public: void start() override { cout << "Electric Car is starting with a button..." << endl; } }; class Motorcycle : public Vehicle { public: void start() override { cout << "Motorcycle is starting with a kick..." << endl; } }; int main() { string vehicleType; // Ask user to choose a vehicle type cout << "Enter the vehicle type (car/motorcycle): "; cin >> vehicleType; Vehicle* vehicle; // Decide the type of vehicle at runtime if (vehicleType == "car") { vehicle = new ElectricCar(); } else if (vehicleType == "motorcycle") { vehicle = new Motorcycle(); } else { vehicle = new Vehicle(); } vehicle->start();  // Outputs depending on user's choice delete vehicle;  // Clean up memory return 0; }

Here’s how the output works based on user input:

Input Output
car Electric Car is starting with a button…
motorcycle Motorcycle is starting with a kick…
anything else Vehicle is starting…

 

This example really shows just how powerful function overriding is when combined with user interaction. It lets us dynamically decide at runtime which version of a function to call depending on the object type.

 

Also Read: OOPs Concepts in C++

Use of Virtual Keyword and Override Keyword in Function Overriding

Here is a question: Haven’t you ever defined a derived class but realised that it’s calling the wrong function?

 

It happens more than we would want to believe.

 

The virtual keyword in C++ is a saver for this very problem.

 

It gives the compiler a notice, “Listen, this function of the base class is intended to be overridden.” Without it, you might inadvertently call the base class the function instead of the derived class function which will bring you some odd behaviour.

 

Consider virtual a sort of signpost that indicates to the program which functions to use at runtime.

 

Let’s look at an example so we can try it out in code:

#include <iostream> using namespace std; class Animal { public: virtual void sound() { cout << "Animal makes a sound" << endl; } }; class Dog : public Animal { public: void sound() override { cout << "Dog barks" << endl; } }; int main() { Animal* myAnimal = new Dog(); myAnimal->sound(); // Outputs: Dog barks delete myAnimal; }

In this code:

  • virtual in the base class ensures that the function in the derived class (Dog) gets invoked, not the one in Animal.
  • If you omit the virtual, the function of the base class would be run, which is not what we want.

Now, let’s talk about override. This keyword was introduced in C++11 to make life a bit easier.

 

We all have made mistakes in our function signatures. Maybe we misspelled something, or perhaps we changed a parameter type by mistake. Without override, C++ will not catch the mistake.

 

Using override, the compiler checks whether our function actually overrides a base class function. If it does, the compiler throws an error. It’s like a safety net.

 

In the same example above, we use override so that the function in Dog properly overrides the one in Animal.

 

In this way, we avoid any surprises down the road.

Real-Life Analogies to Explain Function Overriding in C++

Sometimes, coding terms are abstract, so let’s ground the function overriding with a simple analogy.

 

Imagine you own a restaurant.

There’s a kind of general meal everyone orders, such as dal. Any chef can cook dal, but some may add flavour. Perhaps one spices it differently or uses a different cooking method.

In our analogy,

  • Base class is the general recipe for dal.
  • Class derived from base class is every chef.
  • Function overriding when every chef takes that general recipe and modifies it to make it their own.

Here’s what this looks like in code:

#include <iostream> using namespace std; class Chef { public: virtual void makeDal() { cout << "Making basic dal..." << endl; } }; class SpicyChef : public Chef { public: void makeDal() override { cout << "Making dal with extra spices!" << endl; } }; class MildChef : public Chef { public: void makeDal() override { cout << "Making mild dal with less spices..." << endl; } }; int main() { Chef* chef1 = new SpicyChef(); Chef* chef2 = new MildChef(); chef1->makeDal();  // Outputs: Making dal with extra spices! chef2->makeDal();  // Outputs: Making mild dal with less spices... delete chef1; delete chef2; } Here's the explanation:
  • We have a base class Chef, that cooks simple dal.
  • SpicyChef and MildChef override the function makeDal() to cook their version of the dish.

 

Also Read: Inheritance in C++ 

Compile-Time vs. Runtime Polymorphism in C++

Now, when we talk about polymorphism in C++, we are really talking about two different things: compile-time polymorphism and runtime polymorphism.

 

So, what is the big deal?

 

Compile-time Polymorphism

This is where the compiler already knows which function to call before the program actually runs. This is where function overloading comes in. It will make the correct choice based on the parameters you pass. Everything happens before the program runs.

Runtime Polymorphism

On the other hand, runtime polymorphism is resolved only at run time. It is with this aspect that function overriding comes into play. According to the type of object concerned in a program, at run time the program resolves which version to call of a given function.

 

Compile-time polymorphism is fast as everything gets sorted even before the program actually runs. However, runtime polymorphism is flexible because it lets us decide at run time.

 

Both are used together in C++. It depends on how you use one over the other.

 

Let’s try to understand it further with the help of an example below.

Compile-Time Polymorphism:

#include <iostream> using namespace std; class PrintData { public: void print(int i) { cout << "Integer: " << i << endl; } void print(double f) { cout << "Float: " << f << endl; } }; int main() { PrintData pd; pd.print(5);        // Outputs: Integer: 5 pd.print(10.5);     // Outputs: Float: 10.5 }

Here, the compiler knows at compile-time which version of print() to call.

Runtime Polymorphism Example:

#include <iostream> using namespace std; class Animal { public: virtual void sound() { cout << "Some generic animal sound" << endl; } }; class Cat : public Animal { public: void sound() override { cout << "Cat meows" << endl; } }; int main() { Animal* animalPtr = new Cat(); animalPtr->sound();   // Outputs: Cat meows delete animalPtr; }

In this example, the decision is made at runtime. According to the type of the object concerned, the program checks and calls the relevant function.

 

Also Read: Member Function in C++

Memory Management with C++: Virtual Destructors in Function Overriding

Managing memory in C++ is bad enough; it gets really messy when you do function overriding without care.

 

What happens when you use a derived class’s destructor through a pointer to a base class?

 

If the destructor of the base class is not declared virtual, then its derived class destructor won’t be called. This can result in the loss of resources when working with dynamically allocated resources like files, memory, network connections, etc.

 

That is where virtual destructors come into play.

 

In C++, if a class is intended to be inherited and used polymorphically, its destructor should be virtual. Doing this ensures the right destructor will be called when an object is destroyed by the destructor through a pointer that is actually a pointer to the base class.

 

Here’s a simple reason why this should be done:

#include <iostream> using namespace std; class Base { public: // Virtual destructor ensures proper cleanup virtual ~Base() { cout << "Base class destructor called" << endl; } }; class Derived : public Base { public: ~Derived() { cout << "Derived class destructor called" << endl; } }; int main() { Base* basePtr = new Derived(); delete basePtr;  // Outputs both Derived and Base class destructors }

In this example:

When we delete the Derived object through the Base pointer, both destructors are called in the proper sequence, so clean-up is correct.

 

Now consider what happens when the base class does not declare a virtual destructor. In that case, the derived class destructor would not be invoked, and there’s a resource leak. We can avoid these problems with just one simple keyword when using polymorphism.

 

Therefore, when you are implementing function overriding, it is always important to ask yourself, “Does my base class need a virtual destructor?”

Advantages and Disadvantages of Function Overriding in C++

Function overriding in C++ is a strong tool, but, like every tool, it has its pros and cons.

Advantages of Function Overriding:

  • Polymorphism: It gives the option for runtime polymorphism, which allows flexible and dynamic code.
  • Customisation: You can specialize an inherited behaviour of base class functions to suit a particular class.
  • Code Reusability: You don’t need to write out the entire blocks of code again. You simply override the things that need a change.
  • Readability: Function overriding helps to keep your code organised if you have large class hierarchies.

Limitations of Function Overriding

  • Performance Overhead: Virtual functions entail a slight overhead in performance as the call to the function is resolved at run time.
  • Complexity: Inheritance and overriding may lead to code that is hard to trace owing to poor structure.
  • Memory Management: The memory will leak if not properly managed, for instance, by use of virtual destructors when overriding.

Conclusion

Function overloading in C++ is a great technical tool, which can enhance the flexibility of object-oriented programming. The derived classes can override and, based on their own needs, modify or extend the behaviour defined by the methods of the base classes.

 

The right function is called at runtime using the keyword virtual and virtual destructors prevent memory leaks while working on an inheritance. This technique supports runtime polymorphism that helps programmers in writing efficient, reusable, and maintainable code.

 

If developers master function overriding in C++, they can design more dynamic systems-avoiding redundancy while ensuring robust and predictable program behavior in complex class hierarchies.

 

FAQs
If you forget to mark the base class function as virtual, the base class function will be called instead of the overridden function in the derived class, even if you're using a base class pointer to a derived object.
No, in C++, Constructors cannot be overridden. They are not inherited by derived classes, so they don't participate in polymorphism.
Overloading allows multiple functions having same name but with different signatures in the same scope. Overriding changes the behaviour of an inherited function with the same signature by a derived class.
No, but by using an override keyword, our code becomes safer because we ensure that the function really overrides a method of a base class. If something is wrong-the compiler will notice this.
No, because private methods cannot be accessed in the derived class and thus cannot be overridden.

Updated on October 22, 2024

Link
left dot patternright dot pattern

Programs tailored for your success

Popular

Management

Data Science

Finance

Technology

Future Tech

Upskill with expert articles

View all
Hero Vired logo
Hero Vired is a leading LearnTech company dedicated to offering cutting-edge programs in collaboration with top-tier global institutions. As part of the esteemed Hero Group, we are committed to revolutionizing the skill development landscape in India. Our programs, delivered by industry experts, are designed to empower professionals and students with the skills they need to thrive in today’s competitive job market.
Blogs
Reviews
Events
In the News
About Us
Contact us
Learning Hub
18003093939     ·     hello@herovired.com     ·    Whatsapp
Privacy policy and Terms of use

|

Sitemap

© 2024 Hero Vired. All rights reserved