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 + t2adds twoTimeobjects), but cannot change precedence or associativity. - Friend functions break encapsulation to access private members of multiple classes (e.g., adding
AccountandTransactionobjects). - Overloaded operators must be member functions (except
<<,>>,[],()), and cannot be overloaded for built-in types. - Use cases:
Complexnumbers (+),Stringconcatenation (+), orQueueenqueue/dequeue (<<). - Friend functions are declared inside a class but defined outside, often used for non-member utility functions (e.g.,
printDetails()forEmployee). - 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"]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
- Cannot create new operators: Only existing operators (
+,-,*,<<, etc.) can be overloaded. - Cannot change precedence/associativity: The order of operations remains the same.
- Cannot overload:
- Scope resolution (
::) - Member selection (
.,->) - Sizeof (
sizeof) - Conditional (
?:)
- Scope resolution (
- Must return an object of the same class (for binary operators like
+). - 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:ostreamandTime).friendgrants access to private members.
2. Friend Functions: Breaking Encapsulation for Utility Tasks
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?
- Non-member utility functions: E.g.,
printDetails()forEmployee. - Operator overloading for
<<,>>,[]: These need two arguments (e.g.,cout << object). - Access private members of multiple classes: E.g., adding
AccountandTransaction.
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
eSewa (Nepal):
- Operator Overloading: The
+operator is overloaded in theTransactionclass to add amounts from multiple payments (e.g.,total = payment1 + payment2). - Friend Function: A
validateTransaction()friend function checks if the sender’sAccountbalance is sufficient before processing.
- Operator Overloading: The
Khalti (Nepal):
<<Overloading: When printing transaction receipts,cout << transactionuses an overloaded<<to format details likeamount,timestamp, andmerchant.
Daraz Order Queue:
- Queue Operations: The
+operator is overloaded to add items to aShoppingCart(e.g.,cart1 + cart2merges two carts). - Friend Function: A
calculateTotal()friend function accesses privateOrderandProductmembers to compute discounts.
- Queue Operations: The
Ncell Billing:
==Overloading: Checks if twoBillobjects (for the same phone number) are identical before processing refunds.
NEPSE Stock Prices:
[]Overloading: AStockPortfolioclass usesportfolio["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
Mistakes to Avoid
Forgetting to return
*thisin compound assignment operators:// Wrong: Missing return Time& operator+=(const Time &t) { seconds += t.seconds; // ... }Fix: Always return
*thisfor chaining:return *this;Overloading
=(assignment operator):- Requires deep copying for dynamic memory.
- Must handle self-assignment (
if (this == &other)).
Using friend functions excessively:
- Breaks encapsulation. Use only for non-member utilities.
Not handling carry-over in arithmetic:
- Example: Adding
Timeobjects without checkingseconds >= 60.
- Example: Adding
Exam Tip
For operator overloading:
- Always show the constructor initialization and return type.
- Trace one step of the operation (e.g., adding
Timeobjects). - 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 aComplexclass. - Write a friend function to add two
Distanceobjects. - Differentiate between operator overloading and function overloading.
- Overload
In the real world
- eSewa (Nepal) uses friend functions internally to securely combine
UserandTransactiondata for payment processing (e.g.,totalAmount(user, transaction)), whereTransactiondetails are private but need to be accessed by non-member utility functions for validation. - Khalti’s wallet balance system overloads the
+operator forWalletobjects to handle automatic interest calculations (e.g.,Wallet w3 = w1 + w2;wherew1andw2are separate transactions). - Nepali banks’ ATM systems use operator overloading for
Accountclasses to support operations likeaccount1 - transactionAmount(withdrawal) oraccount1 + depositAmount(deposit), ensuring type safety and encapsulation.
Based on the TU BIT syllabus for Object Oriented Programming (BIT153), unit 6.
Discussion
Loading…