Tag: abstraction in programming

  • What is Polymorphism?

    Learn polymorphism in object-oriented programming (OOP) with clear examples and easy explanations. Understand method overloading, method overriding, runtime and compile-time polymorphism, and how they improve code flexibility, reusability, and scalability.

    Polymorphism is a fundamental concept in object-oriented programming (OOP) that allows a single function, method, or interface to perform different behaviors depending on the object or input.

    In simple terms, polymorphism means “one name, many forms,” where the same operation behaves differently in different situations.

    Polymorphism = “Many Forms”

    In programming, it means:

    One name (function/method) can do different things depending on the object or input.

    Simple Real-Life Example

    Think of the word “run”:

    • You run in a race
    • A machine runs
    • A program runs

    Same word → different meanings → polymorphism

    Programming Example

    • For a circle → it draws a circle
    • For a square → it draws a square

    Same function name → different behavior

    Code Example

    class Shape {
    public:
        void draw() {
            cout << "Drawing shape";
        }
    };
    
    class Circle : public Shape {
    public:
        void draw() {
            cout << "Drawing circle";
        }
    };
    
    class Square : public Shape {
    public:
        void draw() {
            cout << "Drawing square";
        }
    };
    

    What happens?

    Shape* s;
    
    Circle c;
    Square sq;
    
    s = &c;
    s->draw();   // Drawing circle
    
    s = &sq;
    s->draw();   // Drawing square
    

    Same draw() → different outputs
    This is polymorphism

    Why Do We Need Polymorphism?

    • Code Reusability
      Write one function and use it for multiple types of objects, reducing duplicate code.
    • Flexibility in Code
      The same interface can handle different data types or objects, making code adaptable.
    • Cleaner & Maintainable Code
      Reduces complexity and keeps code organized, especially in large projects.
    • Extensibility (Easy to Add Features)
      You can add new classes or behaviors without changing existing code.
    • Supports Dynamic Behavior
      Objects can behave differently at runtime, making programs more realistic.
    • Improves Readability
      Using a common method name makes code easier to understand.
    • Reduces Conditional Statements
      Avoids multiple if-else or switch cases by using a common interface.
    • Better Abstraction
      Focus on what to do, not how it is done, improving design quality.
    • Real-World Modeling
      Helps represent real-world scenarios where the same action behaves differently.

    Types of Polymorphism

    There are two main types:

    Types of Polymorphism

    1.Compile-Time Polymorphism (Static Polymorphism)

    Decided at compile time (before program runs)

    How it works?

    The compiler decides which function to call based on:

    • Number of parameters
    • Type of parameters

    A ) Method Overloading

    Same function name, but:

    • Different number of arguments OR
    • Different data types

    Example (C++)

    class Math {
    public:
        int add(int a, int b) {
            return a + b;
        }
    
        int add(int a, int b, int c) {
            return a + b + c;
        }
    
        float add(float a, float b) {
            return a + b;
        }
    };
    

    What’s happening?

    Math m;
    
    m.add(2, 3);        // calls int version
    m.add(2, 3, 4);     // calls 3-arg version
    m.add(2.5, 3.5);    // calls float version
    

    Compiler chooses function before execution

    B) Operator Overloading

    Operators behave differently based on operands.

    int a = 5 + 3;        // addition
    string s = "Hi " + "Bro"; // concatenation
    

    Same + → different behavior

    Key Features

    • Fast (no runtime decision)
    • No inheritance required
    • Less flexible than runtime polymorphism

    2.Run-Time Polymorphism (Dynamic Polymorphism)

    Decided at runtime (during execution)

    How it works?

    • Uses inheritance
    • Uses function overriding
    • Uses pointers/references
    • Uses virtual functions (C++)

    Method Overriding

    Child class provides its own version of parent function.

    Example (C++)

    class Animal {
    public:
        virtual void sound() {
            cout << "Animal sound";
        }
    };
    
    class Dog : public Animal {
    public:
        void sound() {
            cout << "Dog barks";
        }
    };
    
    class Cat : public Animal {
    public:
        void sound() {
            cout << "Cat meows";
        }
    };
    

    Runtime Behavior

    Animal* a;
    
    Dog d;
    Cat c;
    
    a = &d;
    a->sound();   // Dog barks
    
    a = &c;
    a->sound();   // Cat meows
    

    Decision happens at runtime, not compile time

    What is virtual in C++?

    The virtual keyword is used in a base class to ensure that a function can be overridden in a derived (child) class and that the correct function is called at runtime, not compile time.

    Definition

    virtual enables runtime polymorphism by allowing function calls to be resolved dynamically (during execution).

    Why Do We Need virtual?

    Without virtual, C++ uses early binding (compile-time)
    With virtual, C++ uses late binding (runtime)

    This ensures the program calls the correct function based on the actual object, not the pointer type.

    Without virtual (Problem ❌)

    class Animal {
    public:
        void sound() {
            cout << "Animal sound";
        }
    };
    
    class Dog : public Animal {
    public:
        void sound() {
            cout << "Dog barks";
        }
    };
    
    Animal* a;
    Dog d;
    
    a = &d;
    a->sound();   // ❌ Output: Animal sound
    

    Even though object is Dog, it calls Animal function
    Because binding happens at compile time

    With virtual (Solution ✔️)

    class Animal {
    public:
        virtual void sound() {
            cout << "Animal sound";
        }
    };
    
    class Dog : public Animal {
    public:
        void sound() {
            cout << "Dog barks";
        }
    };
    
    Animal* a;
    Dog d;
    
    a = &d;
    a->sound();   // ✔️ Output: Dog barks
    

    Now correct function is called at runtime

    How virtual Works Internally

    When you use virtual:

    Compiler creates:

    • vtable (Virtual Table) → stores function pointers
    • vptr (Virtual Pointer) → inside each object

    At runtime:

    • Object’s vptr points to correct function in vtable
    • Function call is resolved dynamically

    Key Concepts Related to virtual

    1.Function Overriding

    Child class replaces parent function

    2.Base Class Pointer

    Animal* a;
    

    Needed for runtime polymorphism

    3.Dynamic Binding

    Function call decided at runtime

    Virtual Destructor

    class Base {
    public:
        virtual ~Base() {
            cout << "Base destructor";
        }
    };
    

    Ensures:

    • Proper cleanup of derived class objects
    • Prevents memory leaks

    Pure Virtual Function (Advanced)

    class Shape {
    public:
        virtual void draw() = 0;  // pure virtual
    };
    

    Makes class abstract
    Child class must implement this function

    When to Use virtual?

    • When using inheritance
    • When behavior should change in child classes
    • When using base class pointers/references
    • When implementing polymorphism

    Why virtual is important?

    Without virtual:
    Base class function will always be called ❌

    With virtual:
    Correct child function is called ✔️

    Behind the scenes

    • Uses vtable (virtual table)
    • Each object keeps pointer to correct function
    • Enables dynamic dispatch

    Key Features

    • Flexible
    • Supports real-world modeling
    • Slightly slower (runtime decision)
    • Requires inheritance

    Difference Between Both

    FeatureCompile-TimeRun-Time
    Decision TimeBefore executionDuring execution
    SpeedFasterSlightly slower
    ConceptOverloadingOverriding
    InheritanceNot requiredRequired
    FlexibilityLessMore

    Simple Analogy

    • Compile-time → Teacher assigns roles before play
    • Run-time → Actor changes role on stage dynamically

    Compile-Time Polymorphism

    Other Names:

    • Static Polymorphism
    • Early Binding
    • Static Binding

    All mean the same thing:

    Decision is made before program runs

    Run-Time Polymorphism

    Other Names:

    • Dynamic Polymorphism
    • Late Binding
    • Dynamic Binding

    All mean:

    Decision is made during program execution

    Example WITHOUT virtual ❌ (Compile-Time Binding)

    Code

    #include <iostream>
    using namespace std;
    
    class Animal {
    public:
        void speak() {
            cout << "Animal speaks" << endl;
        }
    };
    
    class Dog : public Animal {
    public:
        void speak() {
            cout << "Dog barks" << endl;
        }
    };
    
    int main() {
        Animal* a;
        Dog d;
    
        a = &d;
        a->speak();   // What will this print?
    
        return 0;
    }
    

    Output

    Animal speaks
    

    Why this happens (Important)

    • Pointer type = Animal*
    • Object type = Dog

    But C++ ignores object type here
    It only looks at pointer type at compile time

    So:

    • Compiler decides → call Animal::speak()
    • This is called Static Binding / Early Binding

    Key Point

    Without virtual, function call depends on pointer type, not actual object.

    Example WITH virtual ✔️ (Run-Time Binding)

    Code

    #include <iostream>
    using namespace std;
    
    class Animal {
    public:
        virtual void speak() {
            cout << "Animal speaks" << endl;
        }
    };
    
    class Dog : public Animal {
    public:
        void speak() {
            cout << "Dog barks" << endl;
        }
    };
    
    int main() {
        Animal* a;
        Dog d;
    
        a = &d;
        a->speak();   // Now what?
    
        return 0;
    }
    

    Output

    Dog barks
    

    Why this happens

    Because of virtual:

    Compiler delays decision until runtime
    It checks actual object type (Dog)
    Calls Dog::speak()

    This is called:

    What Changes Internally?

    Without virtual:

    • Direct function call
    • No extra mechanism
    • Faster
    • No polymorphism

    With virtual:

    C++ creates:

    vtable (Virtual Table)

    • A table storing addresses of virtual functions

    vptr (Virtual Pointer)

    • Hidden pointer inside each object

    Flow at Runtime:

    1. a = &d;
    2. Object d has its own vptr
    3. vptr points to Dog’s vtable
    4. a->speak():
      • Looks into vtable
      • Finds Dog::speak()
      • Calls it ✔️

    Side-by-Side Comparison

    FeatureWithout virtualWith virtual
    BindingCompile-timeRun-time
    Function CalledBased on pointerBased on object
    Polymorphism❌ No✔️ Yes
    SpeedFasterSlightly slower
    FlexibilityLowHigh

    Real-Life Analogy

    Without virtual:
    You call a person by their job title only
    (“Hey Employee”) → always same response

    With virtual:
    You call based on actual person
    (“Hey Developer / Manager”) → different response

    Basic Interview Questions

    • What is polymorphism?
    • What are the types of polymorphism?
    • Difference between compile-time and runtime polymorphism?
    • What is method overloading vs method overriding?
    • What is the virtual keyword in C++?
    • Why do we use virtual functions?

    Core Concept Questions

    • What happens if we don’t use virtual in function overriding?
    • Why do we need a base class pointer for runtime polymorphism?
    • Explain early binding vs late binding.
    • How does C++ decide which function to call?
    • Can we achieve polymorphism without inheritance? (Yes → overloading)

    Code-Based Questions

    Interviewer may give code like this:

    class A {
    public:
        void show() { cout << "A"; }
    };
    
    class B : public A {
    public:
        void show() { cout << "B"; }
    };
    
    int main() {
        A* obj = new B();
        obj->show();
    }
    

    Question:

    What will be the output and why?

    Expected Answer:

    • Output = A (because no virtual)
    • Uses compile-time binding

    Same with virtual

    virtual void show()
    

    Output = B
    Because of runtime polymorphism

    Advanced Questions

    • What is a virtual table (vtable)?
    • What is a virtual pointer (vptr)?
    • How does dynamic dispatch work internally?
    • What is the cost of using virtual functions?
    • Can constructors be virtual? (No ❌)
    • Can destructors be virtual? (Yes ✔️, very important)

    Tricky Questions

    • What happens if base class destructor is not virtual?
      👉 Can cause memory issues / incomplete cleanup
    • Can we override a non-virtual function?
      👉 Yes, but no runtime polymorphism
    • Difference between virtual and pure virtual?
      👉 Pure virtual makes class abstract
    • What is an abstract class?
      👉 Class with at least one pure virtual function

    Scenario-Based Questions

    • When should you use polymorphism in real projects?
    • Give a real-world example of polymorphism.
    • Where have you used virtual functions?
    • How would you design a system using polymorphism?

    Top 5 Must-Prepare Questions

    If you prepare only these, you’re safe 👇

    1. What is polymorphism with example?
    2. Compile-time vs runtime polymorphism
    3. What is virtual and why needed?
    4. Output-based question (with/without virtual)
    5. What is vtable and how it works?

    For detailed understanding of Platform Devices and Drivers on Linux, refer to the Linux documentation on Platform Devices and Drivers .