Polymorphism
Polymorphism • Polymorphism is another fundamental principles of Object-Oriented Programming (OOP) • The term Polymorphism comes from the Greek words poly (meaning “many”) and morph (meaning “forms”). • It refers to the ability of one function, operator, or object to behave in different ways depending on the context. Imagine you're developing a Payment System for an e-commerce platform. Customers can pay using: • Credit Card • Debit Card • PayPal • Cash on Delivery Each payment method processes the payment differently, but they all follow a common interface: makePayment(). Example
Polymorphism Types
Compile-Time Polymorphism • Compile-Time Polymorphism also known as Static Binding is a type of polymorphism where the decision about which method or function to call is made at compile time. • Key Characteristics: • Resolved at compile time • Faster execution • Implemented using: • Function overloading • Operator overloading
Function Overloading • function overloading allows multiple functions to have the same name with different parameter lists (different number or types of parameters). • Allows to define behavior for similar operations with different input types or numbers. • Real-Life Analogy: Imagine a printer machine: • If you give it a document, it prints a document. • If you give it a photo, it prints a photo. • If you give it a PDF, it prints a PDF. • Even though the command is the same—print()—the behavior changes based on the input. • Basic Rules of Function Overloading • Functions must differ by the number or type of parameters. • Return type alone is not enough to overload a function.
class Printer { public: void print(int num) { cout << "Printing number: " << num << endl; } void print(double num) { cout << "Printing decimal: " << num << endl; } void print(string text) { cout << "Printing text: " << text << endl; } }; int main() { Printer p; p.print(10); // Calls print(int) p.print(3.14); // Calls print(double) p.print("Hello World"); // Calls print(string) return 0; } #include <iostream> using namespace std; void print(int x) { cout << "Integer: " << x << endl; } void print(int x, int y) { cout << "Two Integers: " << x << ", " << y << endl; } int main() { print(10); print(10, 20); return 0; } Overloading with Different Number of Parameters Overloading with Different Types of Parameters Function Overloading
// ❌ This will cause an error int add(int x) { return x; } double add(int x) { return (double)x; } This is not allowed because the compiler cannot differentiate based on return type alone. Function Overloading
Summary Aspect Description Same function name Yes Different params Yes – in number or type Different return No – return type alone is not sufficient for overloading Benefit Cleaner code and flexibility
Operator Overloading • Operator Overloading allows to redefine the meaning of operators (+, -, ==, etc.) foruser-defined types like classes or structures. • Purpose: • Make class objects behave more like built-in types by giving intuitive meaning to operators when used with objects. For example: a + b; // for integers // If 'a' and 'b' are objects, we can define '+' to work meaningfully for them too.
Syntax: return_type operator op (arguments) { // your logic here } • operator is a keyword. • op is the operator being overloaded (+, -, etc.). • This can be a member function or a friend function. Operator Overloading
Example 1: Overloading + for a Complex Number Class #include <iostream> using namespace std; class Complex { float real, imag; public: Complex(float r = 0, float i = 0) { real = r; imag = i; } // Overloading + operator Complex operator + (Complex c) { complex c4(real + c.real, imag + c.imag); return c4; } void display() { cout << real << " + " << imag << "i" << endl; } int main() { Complex c1(3.5, 2.5), c2(1.5, 4.5); Complex c3 = c1 + c2; // Uses overloaded + c3.display(); // Output: 5 + 7i return 0; }
Example 2: Overloading == Operator for a Student Class #include <iostream> using namespace std; class Student { int rollNo; string name; public: Student(int r, string n) { rollNo = r; name = n; } bool operator == (Student s) { return (rollNo == s.rollNo && name == s.name); } }; int main() { Student s1(1, "Ali"), s2(1, "Ali"), s3(2, "Ahmed"); if (s1 == s2) cout << "Same student" << endl; else cout << "Different students" << endl; return 0; }
Rules of Operator Overloading 1 You can only overload existing operators. 2 At least one operand must be a user-defined type. 3 Some operators cannot be overloaded: ::, .*, ., sizeof, typeid. 4 You cannot change precedence or associativity. 5 You cannot change the number of operands. 6 When using binary operators overloaded through a member function, the left-hand operand must be an object of the relevant class. Binary operators, overloaded by a member function, take one explicit argument 7 8 Unary operators, overloaded by a friend function, take one argument(the object of the relevant class) 9 Binary operators, overloaded by a friend function take two explicit arguments. 10 Unary operators, overloaded by a friend function, take one argument(the object of the relevant class
Commonly Overloaded Operators in C++ Operator Typical Use Case + Add objects (e.g., vectors, complex numbers) - Subtract objects * Multiply objects == Compare objects <<, >> Input/output [] Indexing () Function-call operator
Run Time Polymorphism • Run Time Polymorphism occurs when the method to be called is determined at run time, not at compile time. • It is also known as dynamic binding or late binding
Run Time Polymorphism • It is achieved by using a combination of : • Function Overriding • Virtual Functions • Base Class Pointers or References to derived class objects
Function Overriding • Real-Life Example: Patient Consultation System • A generic Doctor class with a function consult(). This represents a generic consultation process. • A Dentist specializes in teeth-related consultations • A Cardiologist handles heart-related issues
Function Overriding • Function Overriding is also known as method overriding • Function overriding occurs • when a derived class provides a specific implementation of a function that is already defined in its base class, using the same name, return type, and parameters.
Function Overriding
class baseClass { public: void display() { cout << " Base Class Execute " << endl; } }; using namespace std; int main() { derivedClass obj; obj.display(); return 0; } Function Overriding class derivedClass: public baseClass { public: void display() { cout << " Derived Class Execute and this derive class override the function of base class " << endl; } }; • There must be inheritance (base and derived class). • The signature (name and parameters) of the function must be exactly the same in both base and derived classes.
• A virtual function is a member function in a base class that you expect to override in derived classes. • It allows run-time polymorphism, meaning the function to call is decided at runtime based on the actual type of the object. Virtual Function • Allows one interface to be used for different underlying types. • You can call a method on a base class reference, and the correct derived class version is executed.
•To make a function virtual, the virtual keyword must precede the function declaration in the base class. •The redefinition of the function in any derived class does not require a second use of the virtual keyword. Virtual Function
Diff b/w virtual and non-virtual member Functions •The difference between a non-virtual member function and a virtual member function is, • the non-virtual member functions are resolved at compile time. • Whereas the virtual member functions are resolved at run-time. • The concept of pointers to objects is prior to knowing before implementing the virtual function.
Pointer to Object / Object Pointer •A pointer pointing to a class object is called an object pointer. •Synatx: Class name *variableName •Example: Shape *optr; // in the this declaration optr is a pointer to an object of class Shape. •To refer directly to a member of an object pointed by a pointer we can use the arrow operator (- >) instead of dot ( . ) •Object pointers are useful in creating objects at run time and public members of the class can be accessible by object pointers.
Function overriding with virtual function #include <iostream> using namespace std; class shape { protected: int len, wid, radius; public: virtual int area() { return 0; } }; class triangle:public shape { public: triangle(int l, int w, int r) { len = l; wid = w; radius = r; } int area() { cout << " Triangle of Area: "; return 0.5 * len * wid; } };
Function overriding with virtual function class circle :public shape { public: circle(int l, int w, int r) { len = l; wid = w; radius = r; } int area() { cout << " Circle of Area: "; return 3.14 * radius * radius ; } }; class square :public shape { public: square(int l, int w, int r) { len = l; wid = w; radius = r; } int area() { cout << " Square of Area: "; return len * wid; } };
Function overriding with virtual function (source.cpp) #include<iostream> #include "Header.h" using namespace std; int main() { shape *obj1; shape* obj2; shape* obj3; triangle tri_obj(10,20,30); square sqr_obj(10, 20, 30); circle crl_obj(10, 20, 30); obj1 = &tri_obj; obj2 = &sqr_obj; obj3 = &crl_obj; cout << obj1->area() << endl; cout << obj2->area() << endl; cout << obj3->area() << endl; return 0; }
Output
• In compile-time binding, the decision of which function to call is made at compile time, based on the type of the pointer/reference, not the object it points to. • Run-time polymorphism allows you to decide which function to call at runtime depending on the actual type of the object that a pointer or reference refers to — not the type of the pointer or reference itself.
#include <iostream> using namespace std; class Animal { public: virtual void sound() { // Virtual function cout << "Some animal sound" << endl; } }; class Dog : public Animal { public: void sound() override { cout << "Woof!" << endl; } }; class Cat : public Animal { public: void sound() override { cout << "Meow!" << endl; } }; int main() { Animal* a; // Base class pointer Dog d; Cat c; a = &d; a->sound(); // Output: Woof! (run-time decision) a = &c; a->sound(); // Output: Meow! (run-time decision) return 0; }
#include <iostream> using namespace std; class Animal { public: void sound() { // Virtual function cout << "Some animal sound" << endl; } }; class Dog : public Animal { public: void sound() override { cout << "Woof!" << endl; } }; class Cat : public Animal { public: void sound() override { cout << "Meow!" << endl; } }; int main() { Animal* a; // Base class pointer Dog d; Cat c; a = &d; a->sound(); // Output: Some animal sound (compile-time decision) a = &c; a->sound(); // Output: Meow! (run-time decision) return 0; In compile-time binding, the decision of which function to call is made at compile time, based on the type of the pointer/reference, not the object it points to. If you remove the virtual keyword, then: without the virtual keyword, C++ uses compile-time (or static) binding Even if a points to a Dog, the base class function will be called.
When Does Run-Time Binding Happen? Only when: •The function in the base class is marked as virtual. •The function is overridden in the derived class. •The call is made using a base class pointer or reference.

Polymorphism in Object-oriented Programming

  • 1.
  • 2.
    Polymorphism • Polymorphism isanother fundamental principles of Object-Oriented Programming (OOP) • The term Polymorphism comes from the Greek words poly (meaning “many”) and morph (meaning “forms”). • It refers to the ability of one function, operator, or object to behave in different ways depending on the context. Imagine you're developing a Payment System for an e-commerce platform. Customers can pay using: • Credit Card • Debit Card • PayPal • Cash on Delivery Each payment method processes the payment differently, but they all follow a common interface: makePayment(). Example
  • 3.
  • 4.
    Compile-Time Polymorphism • Compile-TimePolymorphism also known as Static Binding is a type of polymorphism where the decision about which method or function to call is made at compile time. • Key Characteristics: • Resolved at compile time • Faster execution • Implemented using: • Function overloading • Operator overloading
  • 5.
    Function Overloading • functionoverloading allows multiple functions to have the same name with different parameter lists (different number or types of parameters). • Allows to define behavior for similar operations with different input types or numbers. • Real-Life Analogy: Imagine a printer machine: • If you give it a document, it prints a document. • If you give it a photo, it prints a photo. • If you give it a PDF, it prints a PDF. • Even though the command is the same—print()—the behavior changes based on the input. • Basic Rules of Function Overloading • Functions must differ by the number or type of parameters. • Return type alone is not enough to overload a function.
  • 6.
    class Printer { public: voidprint(int num) { cout << "Printing number: " << num << endl; } void print(double num) { cout << "Printing decimal: " << num << endl; } void print(string text) { cout << "Printing text: " << text << endl; } }; int main() { Printer p; p.print(10); // Calls print(int) p.print(3.14); // Calls print(double) p.print("Hello World"); // Calls print(string) return 0; } #include <iostream> using namespace std; void print(int x) { cout << "Integer: " << x << endl; } void print(int x, int y) { cout << "Two Integers: " << x << ", " << y << endl; } int main() { print(10); print(10, 20); return 0; } Overloading with Different Number of Parameters Overloading with Different Types of Parameters Function Overloading
  • 7.
    // ❌ Thiswill cause an error int add(int x) { return x; } double add(int x) { return (double)x; } This is not allowed because the compiler cannot differentiate based on return type alone. Function Overloading
  • 8.
    Summary Aspect Description Same functionname Yes Different params Yes – in number or type Different return No – return type alone is not sufficient for overloading Benefit Cleaner code and flexibility
  • 9.
    Operator Overloading • OperatorOverloading allows to redefine the meaning of operators (+, -, ==, etc.) foruser-defined types like classes or structures. • Purpose: • Make class objects behave more like built-in types by giving intuitive meaning to operators when used with objects. For example: a + b; // for integers // If 'a' and 'b' are objects, we can define '+' to work meaningfully for them too.
  • 10.
    Syntax: return_type operator op(arguments) { // your logic here } • operator is a keyword. • op is the operator being overloaded (+, -, etc.). • This can be a member function or a friend function. Operator Overloading
  • 11.
    Example 1: Overloading+ for a Complex Number Class #include <iostream> using namespace std; class Complex { float real, imag; public: Complex(float r = 0, float i = 0) { real = r; imag = i; } // Overloading + operator Complex operator + (Complex c) { complex c4(real + c.real, imag + c.imag); return c4; } void display() { cout << real << " + " << imag << "i" << endl; } int main() { Complex c1(3.5, 2.5), c2(1.5, 4.5); Complex c3 = c1 + c2; // Uses overloaded + c3.display(); // Output: 5 + 7i return 0; }
  • 12.
    Example 2: Overloading== Operator for a Student Class #include <iostream> using namespace std; class Student { int rollNo; string name; public: Student(int r, string n) { rollNo = r; name = n; } bool operator == (Student s) { return (rollNo == s.rollNo && name == s.name); } }; int main() { Student s1(1, "Ali"), s2(1, "Ali"), s3(2, "Ahmed"); if (s1 == s2) cout << "Same student" << endl; else cout << "Different students" << endl; return 0; }
  • 13.
    Rules of OperatorOverloading 1 You can only overload existing operators. 2 At least one operand must be a user-defined type. 3 Some operators cannot be overloaded: ::, .*, ., sizeof, typeid. 4 You cannot change precedence or associativity. 5 You cannot change the number of operands. 6 When using binary operators overloaded through a member function, the left-hand operand must be an object of the relevant class. Binary operators, overloaded by a member function, take one explicit argument 7 8 Unary operators, overloaded by a friend function, take one argument(the object of the relevant class) 9 Binary operators, overloaded by a friend function take two explicit arguments. 10 Unary operators, overloaded by a friend function, take one argument(the object of the relevant class
  • 14.
    Commonly Overloaded Operatorsin C++ Operator Typical Use Case + Add objects (e.g., vectors, complex numbers) - Subtract objects * Multiply objects == Compare objects <<, >> Input/output [] Indexing () Function-call operator
  • 15.
    Run Time Polymorphism •Run Time Polymorphism occurs when the method to be called is determined at run time, not at compile time. • It is also known as dynamic binding or late binding
  • 16.
    Run Time Polymorphism •It is achieved by using a combination of : • Function Overriding • Virtual Functions • Base Class Pointers or References to derived class objects
  • 17.
    Function Overriding • Real-LifeExample: Patient Consultation System • A generic Doctor class with a function consult(). This represents a generic consultation process. • A Dentist specializes in teeth-related consultations • A Cardiologist handles heart-related issues
  • 18.
    Function Overriding • FunctionOverriding is also known as method overriding • Function overriding occurs • when a derived class provides a specific implementation of a function that is already defined in its base class, using the same name, return type, and parameters.
  • 19.
  • 20.
    class baseClass { public: voiddisplay() { cout << " Base Class Execute " << endl; } }; using namespace std; int main() { derivedClass obj; obj.display(); return 0; } Function Overriding class derivedClass: public baseClass { public: void display() { cout << " Derived Class Execute and this derive class override the function of base class " << endl; } }; • There must be inheritance (base and derived class). • The signature (name and parameters) of the function must be exactly the same in both base and derived classes.
  • 21.
    • A virtualfunction is a member function in a base class that you expect to override in derived classes. • It allows run-time polymorphism, meaning the function to call is decided at runtime based on the actual type of the object. Virtual Function • Allows one interface to be used for different underlying types. • You can call a method on a base class reference, and the correct derived class version is executed.
  • 22.
    •To make afunction virtual, the virtual keyword must precede the function declaration in the base class. •The redefinition of the function in any derived class does not require a second use of the virtual keyword. Virtual Function
  • 23.
    Diff b/w virtualand non-virtual member Functions •The difference between a non-virtual member function and a virtual member function is, • the non-virtual member functions are resolved at compile time. • Whereas the virtual member functions are resolved at run-time. • The concept of pointers to objects is prior to knowing before implementing the virtual function.
  • 24.
    Pointer to Object/ Object Pointer •A pointer pointing to a class object is called an object pointer. •Synatx: Class name *variableName •Example: Shape *optr; // in the this declaration optr is a pointer to an object of class Shape. •To refer directly to a member of an object pointed by a pointer we can use the arrow operator (- >) instead of dot ( . ) •Object pointers are useful in creating objects at run time and public members of the class can be accessible by object pointers.
  • 25.
    Function overriding withvirtual function #include <iostream> using namespace std; class shape { protected: int len, wid, radius; public: virtual int area() { return 0; } }; class triangle:public shape { public: triangle(int l, int w, int r) { len = l; wid = w; radius = r; } int area() { cout << " Triangle of Area: "; return 0.5 * len * wid; } };
  • 26.
    Function overriding withvirtual function class circle :public shape { public: circle(int l, int w, int r) { len = l; wid = w; radius = r; } int area() { cout << " Circle of Area: "; return 3.14 * radius * radius ; } }; class square :public shape { public: square(int l, int w, int r) { len = l; wid = w; radius = r; } int area() { cout << " Square of Area: "; return len * wid; } };
  • 27.
    Function overriding withvirtual function (source.cpp) #include<iostream> #include "Header.h" using namespace std; int main() { shape *obj1; shape* obj2; shape* obj3; triangle tri_obj(10,20,30); square sqr_obj(10, 20, 30); circle crl_obj(10, 20, 30); obj1 = &tri_obj; obj2 = &sqr_obj; obj3 = &crl_obj; cout << obj1->area() << endl; cout << obj2->area() << endl; cout << obj3->area() << endl; return 0; }
  • 28.
  • 29.
    • In compile-timebinding, the decision of which function to call is made at compile time, based on the type of the pointer/reference, not the object it points to. • Run-time polymorphism allows you to decide which function to call at runtime depending on the actual type of the object that a pointer or reference refers to — not the type of the pointer or reference itself.
  • 30.
    #include <iostream> using namespacestd; class Animal { public: virtual void sound() { // Virtual function cout << "Some animal sound" << endl; } }; class Dog : public Animal { public: void sound() override { cout << "Woof!" << endl; } }; class Cat : public Animal { public: void sound() override { cout << "Meow!" << endl; } }; int main() { Animal* a; // Base class pointer Dog d; Cat c; a = &d; a->sound(); // Output: Woof! (run-time decision) a = &c; a->sound(); // Output: Meow! (run-time decision) return 0; }
  • 31.
    #include <iostream> using namespacestd; class Animal { public: void sound() { // Virtual function cout << "Some animal sound" << endl; } }; class Dog : public Animal { public: void sound() override { cout << "Woof!" << endl; } }; class Cat : public Animal { public: void sound() override { cout << "Meow!" << endl; } }; int main() { Animal* a; // Base class pointer Dog d; Cat c; a = &d; a->sound(); // Output: Some animal sound (compile-time decision) a = &c; a->sound(); // Output: Meow! (run-time decision) return 0; In compile-time binding, the decision of which function to call is made at compile time, based on the type of the pointer/reference, not the object it points to. If you remove the virtual keyword, then: without the virtual keyword, C++ uses compile-time (or static) binding Even if a points to a Dog, the base class function will be called.
  • 32.
    When Does Run-TimeBinding Happen? Only when: •The function in the base class is marked as virtual. •The function is overridden in the derived class. •The call is made using a base class pointer or reference.