BIT153 Object Oriented Programming

Object Oriented ProgrammingUnit 612 min read

Operator Overloading & Friend Functions: Syntax, Rules, Real-World Uses

Unit 6 of Object Oriented Programming covers how to redefine operators (like +, -, ) for custom classes, how to use friend functions to access private members, and when to apply each technique—with visual traces, real-world examples (e.g., eSewa’s transaction validation), and code templates for exams.

TAKEAWAYS:

  • Operator overloading lets you define how operators work for your class (e.g., Time t1 + t2 adds two Time objects), but cannot change precedence or associativity.
  • Friend functions break encapsulation to access private members of multiple classes (e.g., adding Account and Transaction objects).
  • Overloaded operators must be member functions (except <<, >>, [], ()), and cannot be overloaded for built-in types.
  • Use cases: Complex numbers (+), String concatenation (+), or Queue enqueue/dequeue (<<).
  • Friend functions are declared inside a class but defined outside, often used for non-member utility functions (e.g., printDetails() for Employee).
  • Exam pitfall: Forgetting to return the object (return *this) in compound assignment operators (+=, -=).

1. Operator Overloading: Redefining Operators for Classes

flowchart TD
    A["Time t1: 2:30:45"] -->|"seconds: 45"| B["Time t2: 1:20:15"]
    B -->|"seconds: 15"| C["operator+(t1, t2)"]
    C --> D["temp.seconds = 45 + 15 = 60"]
    C --> E["temp.minutes = 30 + 20 = 50"]
    C --> F["temp.hours = 2 + 1 = 3"]
    D -->|"carry=1"| G["temp.minutes = 50 + 1 = 51"]
    G -->|"carry=0"| H["temp.hours = 3"]
    H --> I["return Time(4:50:00)"]
    I --> J["Time t3: 4:50:00"]
head[object Object]NULL
State after overloaded + operation (t1 + t2 → t3)
head[object Object][object Object]NULL
State of Time objects before addition (t1 + t2)

What is Operator Overloading?

Operator overloading is a feature in C++ that allows you to redefine how operators work for user-defined types (classes). For example, you can make the + operator add two Time objects instead of integers.

Rules for Operator Overloading

  1. Cannot create new operators: Only existing operators (+, -, *, <<, etc.) can be overloaded.
  2. Cannot change precedence/associativity: The order of operations remains the same.
  3. Cannot overload:
    • Scope resolution (::)
    • Member selection (., ->)
    • Sizeof (sizeof)
    • Conditional (?:)
  4. Must return an object of the same class (for binary operators like +).
  5. Must be a member function (except <<, >>, [], ()).

Syntax

return_type operator symbol(arguments) {
    // Logic
}

Example: Overloading + for a Time class.

Worked Example: Adding Two Time Objects

#include <iostream>
using namespace std;

class Time {
private:
    int hours, minutes, seconds;
public:
    Time() : hours(0), minutes(0), seconds(0) {}
    Time(int h, int m, int s) : hours(h), minutes(m), seconds(s) {}

    // Overload + operator
    Time operator+(const Time &t) {
        Time temp;
        temp.seconds = this->seconds + t.seconds;
        temp.minutes = this->minutes + t.minutes + (temp.seconds / 60);
        temp.hours = this->hours + t.hours + (temp.minutes / 60);
        temp.seconds %= 60;
        temp.minutes %= 60;
        return temp;
    }

    void display() {
        cout << hours << ":" << minutes << ":" << seconds << endl;
    }
};

int main() {
    Time t1(2, 30, 45), t2(1, 20, 15);
    Time t3 = t1 + t2; // Calls overloaded +
    t3.display(); // Output: 3:50:60 → 4:50:00 (after carry-over)
    return 0;
}

Trace Table:

Step t1 (2:30:45) t2 (1:20:15) temp.seconds temp.minutes temp.hours Final t3
1 2:30:45 1:20:15 45 + 15 = 60 30 + 20 = 50 2 + 1 = 3 3:50:60 (carry)
2 (Carry) - - 60 % 60 = 0 50 + 1 = 51 3 + 1 = 4 4:50:00

Visualization of + Overloading:

flowchart TD
    A["Time::operator+(t1, t2)"] --> B["temp.seconds = 45 + 15 = 60"]
    B -->|"if >=60"| C["carry = 1, temp.seconds = 0"]
    A --> D["temp.minutes = 30 + 20 + 1 = 51"]
    D -->|"if >=60"| E["carry = 0, temp.minutes = 51"]
    A --> F["temp.hours = 2 + 1 = 3"]
    F --> G["return Time(4:50:00)"]

Common Overloaded Operators

Operator Typical Use Case Example Class
+ Addition Complex, Time
- Subtraction Distance
<< Output streaming String, Database
== Equality check Student, Account
[] Array-like access String, Matrix
() Function call SmartPointer

Overloading << for Output

friend ostream& operator<<(ostream &os, const Time &t) {
    os << t.hours << ":" << t.minutes << ":" << t.seconds;
    return os;
}

Why friend?

  • << cannot be a member function (requires two arguments: ostream and Time).
  • friend grants access to private members.
head[object Object]NULL
Output stream overloaded for Time object (t3 << cout)

2. Friend Functions: Breaking Encapsulation for Utility Tasks

0[object Object][object Object]1—2—3—
Friend function accessing private balances of two Account objects (h(k) = k mod 4)

What is a Friend Function?

A friend function is a function that is not a member of a class but has access to its private and protected members. It is declared inside the class using the friend keyword.

Why Use Friend Functions?

  1. Non-member utility functions: E.g., printDetails() for Employee.
  2. Operator overloading for <<, >>, []: These need two arguments (e.g., cout << object).
  3. Access private members of multiple classes: E.g., adding Account and Transaction.

Syntax

class ClassName {
private:
    // Data members
public:
    friend return_type functionName(arguments);
};

Definition outside the class:

return_type functionName(arguments) {
    // Access private members
}

Worked Example: Adding Two Account Balances

#include <iostream>
using namespace std;

class Account {
private:
    string name;
    double balance;
public:
    Account(string n, double b) : name(n), balance(b) {}

    // Friend function to add balances
    friend double totalBalance(const Account &a1, const Account &a2);
};

double totalBalance(const Account &a1, const Account &a2) {
    return a1.balance + a2.balance;
}

int main() {
    Account acc1("Ramesh", 10000), acc2("Sita", 15000);
    cout << "Total balance: " << totalBalance(acc1, acc2) << endl;
    return 0;
}

Output:

Total balance: 25000

Visualization of Friend Function Access:

classDiagram
    class Account {
        -string name
        -double balance
        +Account(string, double)
        +friend double totalBalance(const Account&, const Account&)
    }
    Account --> FriendFunction : "Accesses private members"
    class FriendFunction {
        +double totalBalance(const Account& a, const Account& b)
        +return a.balance + b.balance
    }

Friend Functions vs. Member Functions

Feature Friend Function Member Function
Declaration Inside class, outside definition Inside class
Access Can access private/protected members Only its own class’s members
Syntax friend return_type func(args); return_type func(args) { ... }
Use Case Utility functions, operator overloading Class-specific operations
Inheritance Not inherited Inherited by derived classes

3. Real-World Applications

In the Real World

  1. eSewa (Nepal):

    • Operator Overloading: The + operator is overloaded in the Transaction class to add amounts from multiple payments (e.g., total = payment1 + payment2).
    • Friend Function: A validateTransaction() friend function checks if the sender’s Account balance is sufficient before processing.
  2. Khalti (Nepal):

    • << Overloading: When printing transaction receipts, cout << transaction uses an overloaded << to format details like amount, timestamp, and merchant.
  3. Daraz Order Queue:

    • Queue Operations: The + operator is overloaded to add items to a ShoppingCart (e.g., cart1 + cart2 merges two carts).
    • Friend Function: A calculateTotal() friend function accesses private Order and Product members to compute discounts.
  4. Ncell Billing:

    • == Overloading: Checks if two Bill objects (for the same phone number) are identical before processing refunds.
  5. NEPSE Stock Prices:

    • [] Overloading: A StockPortfolio class uses portfolio["NEPSE"] to access the price of a specific stock.

Worked Example: Daraz Shopping Cart Merge

class ShoppingCart {
private:
    string items[10];
    int count;
public:
    ShoppingCart() : count(0) {}

    // Overload + to merge two carts
    ShoppingCart operator+(const ShoppingCart &other) {
        ShoppingCart merged;
        for (int i = 0; i < this->count; i++) {
            merged.items[i] = this->items[i];
        }
        for (int i = 0; i < other.count; i++) {
            merged.items[this->count + i] = other.items[i];
        }
        merged.count = this->count + other.count;
        return merged;
    }

    void display() {
        cout << "Items: ";
        for (int i = 0; i < count; i++) {
            cout << items[i] << " ";
        }
        cout << endl;
    }
};

int main() {
    ShoppingCart cart1, cart2;
    cart1.items[0] = "Laptop"; cart1.count = 1;
    cart2.items[0] = "Mouse"; cart2.count = 1;
    ShoppingCart cart3 = cart1 + cart2; // Merges carts
    cart3.display(); // Output: Items: Laptop Mouse
    return 0;
}

Trace Table:

Step cart1 cart2 merged.items merged.count
1 ["Laptop"] ["Mouse"] ["Laptop"] 1
2 - - ["Laptop", "Mouse"] 2

4. Common Pitfalls and Exam Tips

-1012Valid Overloads (e.g., +, <<)Allowed (e.g., -, *)Invalid (e.g., =, new)
Valid vs. invalid operator overloading examples

Mistakes to Avoid

  1. Forgetting to return *this in compound assignment operators:

    // Wrong: Missing return
    Time& operator+=(const Time &t) {
        seconds += t.seconds;
        // ...
    }
    

    Fix: Always return *this for chaining:

    return *this;
    
  2. Overloading = (assignment operator):

    • Requires deep copying for dynamic memory.
    • Must handle self-assignment (if (this == &other)).
  3. Using friend functions excessively:

    • Breaks encapsulation. Use only for non-member utilities.
  4. Not handling carry-over in arithmetic:

    • Example: Adding Time objects without checking seconds >= 60.

Exam Tip

  • For operator overloading:

    • Always show the constructor initialization and return type.
    • Trace one step of the operation (e.g., adding Time objects).
    • Mention which operators cannot be overloaded (e.g., ::, sizeof).
  • For friend functions:

    • Show the declaration inside the class and definition outside.
    • Explain why it’s not a member function (e.g., needs two arguments).
    • Compare with member functions in a table (as above).
  • Common exam questions:

    • Overload + for a Complex class.
    • Write a friend function to add two Distance objects.
    • Differentiate between operator overloading and function overloading.

In the real world

  • eSewa (Nepal) uses friend functions internally to securely combine User and Transaction data for payment processing (e.g., totalAmount(user, transaction)), where Transaction details are private but need to be accessed by non-member utility functions for validation.
  • Khalti’s wallet balance system overloads the + operator for Wallet objects to handle automatic interest calculations (e.g., Wallet w3 = w1 + w2; where w1 and w2 are separate transactions).
  • Nepali banks’ ATM systems use operator overloading for Account classes to support operations like account1 - transactionAmount (withdrawal) or account1 + depositAmount (deposit), ensuring type safety and encapsulation.

Based on the TU BIT syllabus for Object Oriented Programming (BIT153), unit 6.

Discussion

Loading…