How is run time polymorphism achieved at compile time?


Q.) How is run time polymorphism achieved at compile time?

Subject: Object Oriented Programming and Methodology

Runtime polymorphism achieved at compile-time:

In object-oriented programming, runtime polymorphism allows objects of different classes to respond to the same method call in different ways. This is achieved through method overriding, which occurs when a subclass defines a method with the same name and signature as a method in its superclass. When an object of the subclass is called with the overridden method, the subclass's method is executed instead of the superclass's method.

However, in some cases, it is desirable to achieve polymorphism at compile-time rather than at runtime. This can be done using a technique called compile-time polymorphism, also known as static polymorphism. Compile-time polymorphism is achieved through function overloading, which allows multiple functions with the same name but different parameter types or numbers to coexist in the same program.

When a function call is made, the compiler determines which function to call based on the types and numbers of the arguments passed to the function. This allows the compiler to select the appropriate function at compile-time, eliminating the need for method overriding and runtime dispatch.

Example:

class Shape {
public:
    virtual double area() = 0; // Pure virtual function
};

class Rectangle : public Shape {
public:
    Rectangle(double length, double width) : length(length), width(width) {}
    double area() override { return length * width; }

private:
    double length;
    double width;
};

class Circle : public Shape {
public:
    Circle(double radius) : radius(radius) {}
    double area() override { return M_PI * radius * radius; }

private:
    double radius;
};

int main() {
    Shape* shape1 = new Rectangle(5.0, 10.0);
    Shape* shape2 = new Circle(5.0);

    // Compile-time polymorphism
    std::cout << "Area of rectangle: " << shape1->area() << std::endl;
    std::cout << "Area of circle: " << shape2->area() << std::endl;

    delete shape1;
    delete shape2;

    return 0;
}

In this example, the area() function is declared as a pure virtual function in the Shape base class, which forces the derived classes (Rectangle and Circle) to implement their own versions of the function. This is a form of compile-time polymorphism, as the compiler knows which implementation of the area() function to call based on the type of the object at compile-time.

The main advantage of compile-time polymorphism over runtime polymorphism is that it can improve performance by eliminating the need for dynamic dispatch at runtime. Additionally, it can make the code more readable and easier to maintain, as the programmer can see which function implementation will be called at compile-time.

Additional details:

  • Compile-time polymorphism is also known as static binding or early binding, while runtime polymorphism is also known as dynamic binding or late binding.
  • Compile-time polymorphism is often used in situations where the exact type of object is known at compile-time, while runtime polymorphism is used when the exact type of object is not known until runtime.
  • Compile-time polymorphism can also be achieved using templates in C++ and generics in Java and other programming languages.
  • Some programming languages, such as Haskell and Scala, support a combination of compile-time and runtime polymorphism through the use of type classes and typeclasses, respectively.