5 hours · 7 marks
Operator overloading gives operators (+, -, ==, etc.) additional meaning when applied to objects of a user-defined class — an example of compile-time (static) polymorphism.
| Overloadable | Non-overloadable |
|---|---|
| + - * / % = == != < > <= >= ++ -- [] () -> new delete << >> && || ! ~ & | ^ etc. | :: (scope resolution), . (member access), .* (pointer-to-member), ?: (ternary), sizeof |
returnType operator symbol (parameter list) {
// function body defining new behaviour
}
The keyword operator followed by the operator symbol forms the function name.
class Complex {
float real, imag;
public:
Complex(float r=0, float i=0): real(r), imag(i) {}
Complex operator+(Complex c) { // member function -- 1 explicit argument for binary +
return Complex(real + c.real, imag + c.imag);
}
void show() { cout << real << " + " << imag << "i"; }
};
int main() {
Complex c1(2,3), c2(1,4);
Complex c3 = c1 + c2; // calls c1.operator+(c2)
c3.show(); // 3 + 7i
}
class Complex {
float real, imag;
public:
Complex(float r=0, float i=0): real(r), imag(i) {}
friend Complex operator+(Complex a, Complex b); // non-member -- 2 explicit arguments
};
Complex operator+(Complex a, Complex b) {
return Complex(a.real + b.real, a.imag + b.imag);
}
Non-member (often friend) functions are required when the left operand is not an object of the class (e.g., overloading 2 + c1, or overloading <</>> for streams where the left operand is cout/cin).
Unary operators (-, ++, --, !) act on a single object — as a member function they take zero explicit arguments.
class Counter {
int count;
public:
Counter(int c=0): count(c) {}
Counter operator++() { // prefix ++obj
count++;
return *this;
}
Counter operator++(int) { // postfix obj++ (dummy int parameter distinguishes it)
Counter temp = *this;
count++;
return temp;
}
Counter operator-() { // unary minus: negates count
return Counter(-count);
}
void show() { cout << count; }
};
Binary operators (+, -, ==, etc.) act on two operands — as a member function they take one explicit argument (the right operand; the left operand is the calling object, accessed implicitly via this).
class Vector2D {
float x, y;
public:
Vector2D(float a=0, float b=0): x(a), y(b) {}
Vector2D operator+(Vector2D v) { return Vector2D(x+v.x, y+v.y); }
bool operator==(Vector2D v) { return x==v.x && y==v.y; }
};
Three conversion situations:
operator int() { return value; }.class Fahrenheit {
float temp;
public:
Fahrenheit(float t): temp(t) {} // int/float -> Fahrenheit (converting constructor)
operator float() { return temp; } // Fahrenheit -> float (conversion operator)
};
int main() {
Fahrenheit f = 98.6; // float converted to Fahrenheit object
float t = f; // Fahrenheit object converted to float
}
By default, a single-argument constructor also acts as an implicit conversion from that argument type to the class type, which can cause unintended/surprising conversions. The explicit keyword disables this implicit conversion, allowing the constructor to be used only for direct/explicit initialization.
class Box {
int size;
public:
explicit Box(int s): size(s) {}
};
Box b1(5); // OK — direct initialization
Box b2 = 5; // ERROR — implicit conversion disallowed by 'explicit'
Box b3 = Box(5); // OK — explicit conversion
+ operator to add two complex numbers. 7 marksComplex example in section 4.3.int + object or to overload stream operators <</>>.Distance (feet/inches). 6 marksclass Distance {
int feet;
public:
Distance(int f=0): feet(f) {}
Distance operator-() { return Distance(-feet); } // unary: no explicit argument
void show() { cout << feet; }
};
int main() {
Distance d1(10), d2;
d2 = -d1;
d2.show(); // -10
}
:: (scope resolution), . (member access / dot), .* (pointer-to-member access), ?: (ternary conditional), and sizeof cannot be overloaded (see section 4.1).++obj is overloaded as operator++() with no parameters — it increments and then returns the updated object. Postfix obj++ is overloaded as operator++(int) — the dummy int parameter (never used) exists purely to let the compiler distinguish it from the prefix version; it typically saves the old state in a temporary, increments, and returns the temporary (old value). See section 4.5.explicit cannot be used for implicit type conversions — it can only be invoked through direct or explicit initialization. It is used to prevent accidental, unintended conversions that could introduce subtle bugs (e.g., passing a plain int where a class object was expected). See section 4.8.operator type() defined inside the class, with no return type specified and no arguments. Class-to-class conversion uses either a converting constructor in the destination class or a conversion operator in the source class (see section 4.7).== operator to compare two objects of a class Point. 5 marksclass Point {
int x, y;
public:
Point(int a=0, int b=0): x(a), y(b) {}
bool operator==(Point p) { return (x==p.x && y==p.y); }
};
int main() {
Point p1(2,3), p2(2,3);
if (p1 == p2) cout << "Equal";
else cout << "Not equal";
}