ScholarQuill logoScholarQuillUniversity Notes
  • Notes
  • Past Papers
  • Blogs
  • Todo
Login
ScholarQuill logoScholarQuillUniversity Notes
Login
NotesPast PapersBlogsTodo
More
SubjectsDiscussionCGPA CalculatorGPA CalculatorStudent PortalCourse Outline
About
About usPrivacy PolicyReportContact
Notes
Past Papers
Blogs
Todo
Analytics
    Current Subject
    🧩
    Object Oriented Programming
    CSI-312
    Progress0 / 16 topics
    Topics
    1. Evolution of Object-Oriented (OO) Programming2. Object-Oriented (OO) Concepts and Principles3. Problem Solving in OO Paradigm4. OO Programme Design Process5. Classes6. Methods7. Objects and Encapsulation8. Constructors and Destructors9. Operator Overloading10. Function Overloading11. Virtual Functions12. Derived Classes13. Inheritance14. Polymorphism15. I/O and File Processing16. Exception Handling
    CSI-312›Operator Overloading
    Object Oriented ProgrammingTopic 9 of 16

    Operator Overloading

    8 minread
    1,378words
    Intermediatelevel

    Operator Overloading in C++

    Operator overloading is a feature in C++ that allows developers to define or modify the behavior of existing operators for user-defined types (such as classes). In other words, you can overload operators to work with objects of custom classes, enabling more intuitive and flexible interactions with these objects.

    What is Operator Overloading?

    Operator overloading means defining how an operator (such as +, -, *, =, etc.) works for objects of a class. By overloading operators, you can enable them to perform operations like addition, subtraction, or comparison on objects in a way that makes sense for your data types.

    Syntax of Operator Overloading:

    To overload an operator, you need to define a special function called the operator function. The general syntax of an overloaded operator function is:

    return_type operator<operator_symbol>(parameter_list)
    

    Where:

    • operator_symbol is the operator being overloaded (such as +, -, *, etc.).
    • parameter_list includes the arguments for the operation (usually objects of the same or related classes).
    • The return_type is the type of the result of the operation (which could be a class or primitive type).

    Example: Overloading the + Operator

    Let’s start with an example that overloads the + operator for a Complex class, where we want to add two complex numbers using the + operator.

    #include <iostream>
    using namespace std;
    
    class Complex {
    private:
        float real;
        float imag;
    
    public:
        // Constructor to initialize complex number
        Complex(float r = 0, float i = 0) : real(r), imag(i) {}
    
        // Overloading the + operator to add two complex numbers
        Complex operator+(const Complex &other) {
            Complex temp;
            temp.real = real + other.real;
            temp.imag = imag + other.imag;
            return temp;  // Return the new complex number
        }
    
        // Method to display complex number
        void display() {
            cout << real << " + " << imag << "i" << endl;
        }
    };
    
    int main() {
        Complex c1(3.4, 5.6), c2(1.2, 2.3), c3;
        
        // Using overloaded + operator to add two complex numbers
        c3 = c1 + c2;  // This invokes the operator+ method
        
        // Display the result
        c3.display();  // Output: 4.6 + 7.9i
        
        return 0;
    }
    

    Explanation of the Example:

    • Constructor: The Complex class has a constructor to initialize the real and imaginary parts of the complex number.
    • Operator Overloading: The operator+ is overloaded to add two Complex objects. It creates a new Complex object temp, where the real and imaginary parts are added together and returned.
    • Usage: In the main function, the + operator is used to add two Complex numbers (c1 and c2), which invokes the overloaded operator+ function.

    Types of Operators That Can Be Overloaded

    C++ allows overloading of most operators, but some operators cannot be overloaded (like ::, .*, sizeof, and ?:). Here’s a list of common operators that can be overloaded:

    • Arithmetic Operators: +, -, *, /, %, etc.
    • Relational Operators: ==, !=, <, >, <=, >=
    • Logical Operators: &&, ||, !
    • Assignment Operators: =, +=, -=, *=, /=, etc.
    • Increment and Decrement Operators: ++, --
    • Indexing Operator: []
    • Function Call Operator: ()
    • Stream Insertion and Extraction Operators: <<, >>
    • Unary Operators: -, +, !, ~

    Overloading Assignment Operator (=)

    One common operator to overload is the assignment operator (=), especially when your class manages dynamic resources. If you don’t overload it properly, the default assignment may result in shallow copying, leading to problems like double freeing memory.

    Here’s an example where we overload the assignment operator for a class that manages a dynamically allocated memory resource:

    Example: Overloading the Assignment Operator

    #include <iostream>
    #include <cstring>  // For strcpy and strlen
    using namespace std;
    
    class String {
    private:
        char *str;  // Pointer to dynamically allocated memory
    
    public:
        // Constructor to allocate memory
        String(const char *s = "") {
            str = new char[strlen(s) + 1];  // Allocate memory for the string
            strcpy(str, s);  // Copy the string
        }
    
        // Copy constructor
        String(const String &other) {
            str = new char[strlen(other.str) + 1];  // Allocate memory for the new string
            strcpy(str, other.str);  // Copy the string
        }
    
        // Overloading the assignment operator
        String& operator=(const String &other) {
            if (this != &other) {  // Check for self-assignment
                delete[] str;  // Free the existing memory
    
                str = new char[strlen(other.str) + 1];  // Allocate new memory
                strcpy(str, other.str);  // Copy the string
            }
            return *this;  // Return *this to support chained assignment
        }
    
        // Destructor to release memory
        ~String() {
            delete[] str;
        }
    
        // Display the string
        void display() {
            cout << str << endl;
        }
    };
    
    int main() {
        String str1("Hello");
        String str2;
        str2 = str1;  // Invokes the overloaded assignment operator
        str2.display();  // Output: Hello
    
        return 0;
    }
    

    Explanation of the Example:

    • Constructor: The constructor allocates memory dynamically to store a string.
    • Copy Constructor: A custom copy constructor is defined to ensure deep copying of the string when a new object is created from an existing one.
    • Assignment Operator (=): The overloaded assignment operator checks for self-assignment (if (this != &other)), releases any memory already allocated by the object, allocates new memory, and copies the string.
    • Destructor: The destructor frees the dynamically allocated memory when the object is destroyed.

    Important Considerations When Overloading Operators

    1. Self-Assignment Check: Always check for self-assignment in the overloaded assignment operator to avoid deallocating memory before it is copied.

      if (this != &other) { 
          // Perform the assignment
      }
      
    2. Returning *this: In the assignment operator, return the object itself (i.e., *this) so that assignment operations can be chained. For example: a = b = c;.

    3. Efficiency: Be careful with operators that involve resource management (like dynamic memory). Improper handling of memory can lead to memory leaks or undefined behavior.

    4. Unary and Binary Operators: Be mindful of how you overload unary and binary operators. Unary operators operate on one operand (e.g., -a), and binary operators work on two operands (e.g., a + b).


    Example of Overloading Unary - and Binary + Operators

    #include <iostream>
    using namespace std;
    
    class Point {
    private:
        int x, y;
    
    public:
        // Constructor to initialize the point
        Point(int x = 0, int y = 0) : x(x), y(y) {}
    
        // Overloading unary minus operator to negate coordinates
        Point operator-() {
            return Point(-x, -y);
        }
    
        // Overloading binary plus operator to add two points
        Point operator+(const Point &p) {
            return Point(x + p.x, y + p.y);
        }
    
        // Method to display point coordinates
        void display() {
            cout << "(" << x << ", " << y << ")" << endl;
        }
    };
    
    int main() {
        Point p1(1, 2), p2(3, 4);
    
        // Unary operator-
        Point p3 = -p1;  // Negates coordinates of p1
        p3.display();  // Output: (-1, -2)
    
        // Binary operator+
        Point p4 = p1 + p2;  // Adds the coordinates of p1 and p2
        p4.display();  // Output: (4, 6)
    
        return 0;
    }
    

    Explanation:

    • Unary -: Negates the coordinates of a Point object.
    • Binary +: Adds the coordinates of two Point objects and returns the result as a new Point.

    Conclusion

    Operator overloading allows you to define custom behavior for operators when applied to objects of user-defined classes. This can make your code more intuitive and readable, particularly when dealing with mathematical or logical operations involving objects. However, it's important to implement operator overloading carefully, especially when dealing with resources like memory management or when overloading operators that modify the state of the object.

    Previous topic 8
    Constructors and Destructors
    Next topic 10
    Function Overloading

    Past Papers

    Open this section to load past papers

    Click on Show Past Papers to see past papers.
    On This Page
      Reading Stats
      Est. reading time8 min
      Word count1,378
      Code examples0
      DifficultyIntermediate