Function Overriding in C++



A function is a collection of code blocks with instructions inside, which is used for specific tasks. It is meant to be reused as per requirement, making the division of complex problems into smaller and manageable pieces.

What is Function Overriding in C++?

Function overriding is a concept of object-oriented programming which allows a derived class to redefine a function that is already defined in the base class.

Here the method's name and parameters remain the same, but the derived class changes its behavior to suit their specific needs.

Example

Lets consider these 2 functions; one is base class (a) and another is derived class (b), and the function (c) is the main(), where we are implementing overriding −

Function (a)

 class base { public: void notice( ) cout << "This is my Base Class" ; } 

Function (b)

 class derived: public base { public: void notice( ) cout << "This is my Derived Class"; } 

Function (c)

 void main ( ) { // creating an object for base class and calling it base b; b.notice( ); // creating an object for derived class and calling it derived d ; d.notice( ); } 

Overriding Explanation

  • Here, we have created an object of base and derived class of 'function (a) and function(b)' respectively and called them inside function (c) as shown, "b.notice() and d.notice()" respectively.
  • In this case, for derived class d.notice() will be executed first because it represents the most up-to-date or updated version of the function
  • However, if we create an object of the base class as shown in function(c) "b.msg()" and call it, it will use the original version from the base class.

In short, function overriding occurs when the functionality of the base class is redefined in the derived class. When an object of the derived class is created, it will call the updated function from the derived class, meaning that the base class function (a) is overridden by the derived class function (b).

Function overriding is an essential concept in object-oriented programming, enabling polymorphism and dynamic binding.

Example of Function Overriding

Below is a simple example illustrating how overriding works

 #include <iostream> using namespace std; // Base class class Shape { public: // Virtual method to be overridden virtual void draw() const { cout << "Drawing a shape" << endl; } }; // Derived class Circle class Circle : public Shape { public: // Overriding the base class method void draw() const override { cout << "Drawing a circle" << endl; } }; // Derived class Square class Square : public Shape { public: // Overriding the base class method void draw() const override { cout << "Drawing a square" << endl; } }; // Main function int main() { Shape* shapePtr; Circle circle; Square square; // Point to Circle and call draw() shapePtr = &circle; shapePtr->draw(); // Outputs: Drawing a circle // Point to Square and call draw() shapePtr = &square; shapePtr->draw(); // Outputs: Drawing a square return 0; } 

Output

 Drawing a circle Drawing a square 

Function Overriding Vs. Function Overloading

Although Function Overriding and Function Overloading are essential key-concepts of Object-oriented programming in C++, they both do serve different purposes.

Function overriding allows derived class to get new implementation of method to its previously defined base class, as having different scopes(base class and derived class) where it resolved at runtime polymorphism(dynamic binding), it only take place in presence of inheritance and can overridden just once, where execution speed relatively slower.

Whereas, function overloading enables you to create multiple functions with the same name but different parameter lists within the same scope. It is resolved at compile time polymorphism (static binding), where presence of inheritance is not important, These functions can be overloaded multiple times and execution speed tends to be faster comparatively.

Advanced Overriding Concepts

Heres a list of additional sub topics covering advanced overriding concepts −

1. Virtual Destructor

A virtual destructor makes sure that the destructor of the derived class will be executed when an object is removed through a base class pointer. Function overriding with virtual destructors avoids resource leaks and unpredictable behavior by ensuring proper deletion of objects through base class pointers.

Example

 #include <iostream> using namespace std; class BaseClass { public: virtual ~BaseClass() { // Virtual destructor cout << "BaseClass destructor" << endl; } }; class DerivedClass : public BaseClass { public: ~DerivedClass() override { // Overriding destructor cout << "DerivedClass destructor" << endl; } }; int main() { BaseClass* a = new DerivedClass(); // Calls DerivedClass's destructor followed // by BaseClass's destructor delete a; return 0; } 

Output

 DerivedClass destructor BaseClass destructor 

2. Covariant Return Value Types

Covariant return types allow derived classes to override a method and return a more specific type than the base class method. This means that a derived class can return a pointer or reference to a more derived type, rather than just the base type. Function overriding improves flexibility and precision in object-oriented programming.

Note

In the derived class, the return type must be a pointer or reference to a type that is derived from the return type of the base class.

Example

 #include <iostream> using namespace std; class Vehicle { public: // Final method virtual void honk() final { cout << "Vehicle honk: Beep beep!" << endl; } }; class SportsCar : public Vehicle { // Cannot override honk() here }; int main() { Vehicle* v = new SportsCar(); v->honk(); // Calls Vehicle's honk() method delete v; return 0; } 

Output

 Vehicle honk: Beep beep! 

3. Overriding and final Keyword alternate word

The `final` keyword stops any further subclassing of a class or overriding of a method.

Function overriding with the `final` keyword is important because it guarantees that a class or method cannot be further changed or extended.

Example

 #include <iostream> using namespace std; class Subject { public: // Final method virtual void examType() final { cout << "This subject has a written exam." << endl; } }; class Math : public Subject { // Cannot override examType() here }; int main() { Subject* s = new Math(); s->examType(); // Calls Subject's examType() method delete s; return 0; } 

Output

 This subject has a written exam. 

4. Virtual Inheritance

Virtual inheritance in C++ tackles problems which arise with multiple inheritance, particularly the diamond problem. It ensures that when a class inherits from several base classes which have a common ancestor, only a single instance of that common base class is created.

Virtual inheritance makes sure that only one copy of a base class is used when multiple derived classes share that base class in a hierarchy.

Example

 #include <iostream> using namespace std; class Base { public: void present() { cout << "Display from Base class" << endl; } }; class A : virtual public Base { }; class B : virtual public Base { }; class Final : public A, public B { public: void get() { cout << "Display from Final class" << endl; } }; int main() { Final obj; // Displays: Display from Base class obj.present(); // Displays: Display from Final class obj.get(); return 0; } 

Output

 Display from Base class Display from Final class 

Advantages of Function Overriding

1. Polymorphism

Overriding enables polymorphism by letting objects of different derived classes be treated as instances of a base class. This allows dynamic method binding at runtime, where the correct method implementation is chosen based on the object type, thus enhancing flexibility and adaptability, making it a fundamental component of polymorphism.

2. Code Reusability

Developers can utilize existing code by inheriting methods from a base class and customizing them in derived classes. This approach fosters a more streamlined and organized code structure.

3. Maintainability

Overriding promotes a modular design by encapsulating various functionalities within distinct classes. This approach simplifies understanding, maintaining, updating and extending the code.

4. Design Patterns

Overriding plays a key role in the Template Method pattern by defining the overall structure of an algorithm in a base class, while permitting subclasses to override specific steps.

The Strategy pattern leverages overriding to encapsulate various algorithms and enable their interchange at runtime.

5. Memory Management

When using inheritance and dynamic memory allocation, virtual destructors are vital for proper memory management. Overriding the destructor in derived classes ensures that resources allocated by the base and derived classes are correctly deallocated, which prevents memory leaks and ensures clean resource release.

Advertisements
close