Polymorphism is one of the core concepts of Object-Oriented Programming (OOP) in C++. It allows objects of different classes to be treated as objects of a common base class. Polymorphism is mainly achieved through inheritance and can be categorized into two types: compile-time (or static) polymorphism and run-time (or dynamic) polymorphism.
Key Concepts
-
Compile-time Polymorphism:
- Achieved through function overloading and operator overloading.
- The decision of which function to call is made at compile time.
-
Run-time Polymorphism:
- Achieved through inheritance and virtual functions.
- The decision of which function to call is made at run time.
Compile-time Polymorphism
Function Overloading
Function overloading allows multiple functions with the same name but different parameters to be defined. The correct function to call is determined by the arguments passed.
#include <iostream> using namespace std; class Print { public: void show(int i) { cout << "Integer: " << i << endl; } void show(double d) { cout << "Double: " << d << endl; } void show(string s) { cout << "String: " << s << endl; } }; int main() { Print p; p.show(5); // Calls show(int) p.show(5.5); // Calls show(double) p.show("Hello"); // Calls show(string) return 0; }
Operator Overloading
Operator overloading allows you to redefine the way operators work for user-defined types.
#include <iostream> using namespace std; class Complex { private: float real; float imag; public: Complex() : real(0), imag(0) {} Complex(float r, float i) : real(r), imag(i) {} // Overload + operator to add two Complex objects Complex operator + (const Complex& obj) { Complex temp; temp.real = real + obj.real; temp.imag = imag + obj.imag; return temp; } void display() { cout << "Real: " << real << " Imaginary: " << imag << endl; } }; int main() { Complex c1(3.3, 4.4), c2(1.1, 2.2); Complex c3 = c1 + c2; // Calls overloaded operator+ c3.display(); return 0; }
Run-time Polymorphism
Virtual Functions
Virtual functions allow derived classes to override methods in the base class. The function to be called is determined at run time based on the type of the object pointed to by the base class pointer.
#include <iostream> using namespace std; class Base { public: virtual void show() { cout << "Base class show function" << endl; } void display() { cout << "Base class display function" << endl; } }; class Derived : public Base { public: void show() override { cout << "Derived class show function" << endl; } void display() { cout << "Derived class display function" << endl; } }; int main() { Base* basePtr; Derived derivedObj; basePtr = &derivedObj; // Virtual function, binded at runtime basePtr->show(); // Calls Derived class show function // Non-virtual function, binded at compile time basePtr->display(); // Calls Base class display function return 0; }
Pure Virtual Functions and Abstract Classes
A pure virtual function is a function declared in a base class that has no definition relative to the base class. A class containing at least one pure virtual function is considered an abstract class and cannot be instantiated.
#include <iostream> using namespace std; class Shape { public: virtual void draw() = 0; // Pure virtual function }; class Circle : public Shape { public: void draw() override { cout << "Drawing Circle" << endl; } }; class Square : public Shape { public: void draw() override { cout << "Drawing Square" << endl; } }; int main() { Shape* shape1 = new Circle(); Shape* shape2 = new Square(); shape1->draw(); // Calls Circle's draw shape2->draw(); // Calls Square's draw delete shape1; delete shape2; return 0; }
Practical Exercises
Exercise 1: Function Overloading
Task: Create a class Calculator
with overloaded methods add
that can add two integers, two doubles, and two strings (concatenation).
Solution:
#include <iostream> using namespace std; class Calculator { public: int add(int a, int b) { return a + b; } double add(double a, double b) { return a + b; } string add(string a, string b) { return a + b; } }; int main() { Calculator calc; cout << "Sum of integers: " << calc.add(5, 3) << endl; cout << "Sum of doubles: " << calc.add(5.5, 3.3) << endl; cout << "Concatenation of strings: " << calc.add("Hello, ", "World!") << endl; return 0; }
Exercise 2: Virtual Functions
Task: Create a base class Animal
with a virtual function sound
. Derive two classes Dog
and Cat
from Animal
and override the sound
function to print "Bark" and "Meow" respectively.
Solution:
#include <iostream> using namespace std; class Animal { public: virtual void sound() = 0; // Pure virtual function }; class Dog : public Animal { public: void sound() override { cout << "Bark" << endl; } }; class Cat : public Animal { public: void sound() override { cout << "Meow" << endl; } }; int main() { Animal* animal1 = new Dog(); Animal* animal2 = new Cat(); animal1->sound(); // Calls Dog's sound animal2->sound(); // Calls Cat's sound delete animal1; delete animal2; return 0; }
Common Mistakes and Tips
- Forgetting to use the
virtual
keyword: If you forget to declare a function asvirtual
in the base class, the derived class's function will not override it, leading to unexpected behavior. - Not using
override
keyword: Using theoverride
keyword in derived classes helps catch errors at compile time if the base class function signature changes. - Memory leaks: Always ensure to delete dynamically allocated memory to avoid memory leaks.
Conclusion
Polymorphism is a powerful feature in C++ that allows for flexible and reusable code. By understanding and utilizing both compile-time and run-time polymorphism, you can create more dynamic and efficient programs. In the next section, we will delve into more advanced topics in C++ to further enhance your programming skills.
C++ Programming Course
Module 1: Introduction to C++
- Introduction to C++
- Setting Up the Development Environment
- Basic Syntax and Structure
- Variables and Data Types
- Input and Output
Module 2: Control Structures
Module 3: Functions
Module 4: Arrays and Strings
Module 5: Pointers and References
- Introduction to Pointers
- Pointer Arithmetic
- Pointers and Arrays
- References
- Dynamic Memory Allocation
Module 6: Object-Oriented Programming
- Introduction to OOP
- Classes and Objects
- Constructors and Destructors
- Inheritance
- Polymorphism
- Encapsulation and Abstraction
Module 7: Advanced Topics
- Templates
- Exception Handling
- File I/O
- Standard Template Library (STL)
- Lambda Expressions
- Multithreading