Compile-time Polymorphism: Function Overloading and Operator Overloading in C++


In C++, compile-time polymorphism allows the compiler to resolve function calls at the time of compilation. This can be achieved using function overloading and operator overloading. Both of these features provide flexibility in your code by allowing you to define multiple functions or operators with the same name, but with different parameters or behaviors.

Function Overloading

Function overloading is the ability to define multiple functions with the same name, but with different parameter types or different numbers of parameters. The compiler will determine which function to call based on the arguments passed at compile time.

Example of Function Overloading

    #include <iostream>

    class Printer {
    public:
        void print(int i) {
            std::cout << "Printing integer: " << i << std::endl;
        }

        void print(double d) {
            std::cout << "Printing double: " << d << std::endl;
        }

        void print(const char* str) {
            std::cout << "Printing string: " << str << std::endl;
        }
    };

    int main() {
        Printer printer;
        printer.print(10);           // Calls print(int)
        printer.print(3.14);         // Calls print(double)
        printer.print("Hello!");     // Calls print(const char*)
        return 0;
    }
        

Output:

    Printing integer: 10
    Printing double: 3.14
    Printing string: Hello!
        

Explanation

In this example, we have overloaded the print function with three different versions. The correct version is selected by the compiler based on the type of argument passed. If you pass an integer, the function that accepts an integer will be called, and similarly for other types.

Operator Overloading

Operator overloading is the process of providing a special meaning to an operator for user-defined types. By overloading operators, you can redefine their behavior when applied to objects of your classes. This makes it possible to use operators with objects just like you would with built-in types.

Example of Operator Overloading

Here, we will overload the + operator to add two objects of a class Complex.

    #include <iostream>

    class Complex {
    private:
        int real, imag;
    public:
        Complex() : real(0), imag(0) {}  // Default constructor

        Complex(int r, int i) : real(r), imag(i) {}  // Parameterized constructor

        // Overloading the '+' operator
        Complex operator+(const Complex& obj) {
            Complex temp;
            temp.real = real + obj.real;
            temp.imag = imag + obj.imag;
            return temp;
        }

        void display() {
            std::cout << "Real: " << real << " Imaginary: " << imag << std::endl;
        }
    };

    int main() {
        Complex c1(3, 4), c2(1, 2);
        Complex c3 = c1 + c2;  // Using the overloaded '+' operator
        c3.display();           // Displays the result of addition
        return 0;
    }
        

Output:

    Real: 4 Imaginary: 6
        

Explanation

In this example, the + operator has been overloaded to add two Complex objects. The overloaded operator operator+ takes another Complex object as a parameter and returns a new Complex object with the sum of the real and imaginary parts.

Why Use Operator Overloading?

Operator overloading improves the readability and maintainability of your code. It allows you to use familiar operators, like +, -, and ==, with user-defined types, making your objects behave more like built-in types. It makes your code easier to write and understand, especially when working with complex data types.

Example of Overloading the '==' Operator

    #include <iostream>

    class Box {
    private:
        int length, width, height;
    public:
        Box(int l, int w, int h) : length(l), width(w), height(h) {}

        // Overloading the '==' operator to compare two boxes
        bool operator==(const Box& b) {
            return (length == b.length && width == b.width && height == b.height);
        }

        void display() {
            std::cout << "Length: " << length << " Width: " << width << " Height: " << height << std::endl;
        }
    };

    int main() {
        Box box1(3, 4, 5), box2(3, 4, 5), box3(4, 5, 6);

        if (box1 == box2) {
            std::cout << "Box1 and Box2 are equal." << std::endl;
        } else {
            std::cout << "Box1 and Box2 are not equal." << std::endl;
        }

        if (box1 == box3) {
            std::cout << "Box1 and Box3 are equal." << std::endl;
        } else {
            std::cout << "Box1 and Box3 are not equal." << std::endl;
        }

        return 0;
    }
        

Output:

    Box1 and Box2 are equal.
    Box1 and Box3 are not equal.
        

Explanation

In this example, we overloaded the == operator to compare two Box objects. The overloaded operator checks if the dimensions (length, width, height) of both boxes are the same. This allows you to use the == operator in a natural way with Box objects.

Conclusion

Compile-time polymorphism, through function overloading and operator overloading, helps make your C++ code more readable and intuitive. By allowing the same function or operator to be used with different types of arguments, you can create more flexible and maintainable code. Function overloading improves code reusability, and operator overloading allows user-defined types to interact using standard operators, making your classes more natural to use.





Advertisement