Programming Language & Its Applications β Features of Object-Oriented Programming, NEC licence examination syllabus (Nepal Engineering Council).
Giving +, == and [] a meaning for your own types β so your classes read like built-in ones.
cout << x is operator<<. str1 + str2 is operator+ on std::string. v[3] is operator[] on std::vector. Even *it and ++it on an iterator are overloaded operators β which is exactly why a range-based for loop works on any container. Python does the same thing with __add__ and __getitem__; Java deliberately refused, which is why BigDecimal arithmetic reads as a.add(b).multiply(c) instead of a + b * c. Search "why Java has no operator overloading" β the arguments on both sides are genuinely good, and they are about readability versus abuse.#include <iostream>
#include <cmath>
class Complex {
double re, im;
public:
Complex(double r = 0, double i = 0) : re(r), im(i) {}
/* UNARY minus β one operand, no parameters */
Complex operator-() const { return Complex(-re, -im); }
/* compound assignment β MEMBER, returns *this by ref */
Complex& operator+=(const Complex &o) {
re += o.re; im += o.im;
return *this;
}
Complex& operator*=(const Complex &o) {
double r = re*o.re - im*o.im;
im = re*o.im + im*o.re;
re = r;
return *this;
}
/* PREFIX ++ : returns a reference to the modified object */
Complex& operator++() { ++re; return *this; }
/* POSTFIX ++ : the int parameter is a DUMMY that exists
only to distinguish it. Returns the OLD value. */
Complex operator++(int) { Complex old = *this; ++re;
return old; }
double abs() const { return std::sqrt(re*re + im*im); }
/* free functions need access to re/im */
friend Complex operator+(const Complex&, const Complex&);
friend Complex operator*(const Complex&, const Complex&);
friend bool operator==(const Complex&, const Complex&);
friend std::ostream& operator<<(std::ostream&, const Complex&);
};
/* binary + implemented in terms of += : write it once */
Complex operator+(const Complex &a, const Complex &b) {
Complex r = a; r += b; return r;
}
Complex operator*(const Complex &a, const Complex &b) {
Complex r = a; r *= b; return r;
}
bool operator==(const Complex &a, const Complex &b) {
return a.re == b.re && a.im == b.im;
}
bool operator!=(const Complex &a, const Complex &b) {
return !(a == b); /* reuse ==, never duplicate */
}
std::ostream& operator<<(std::ostream &os, const Complex &c) {
os << c.re << (c.im < 0 ? " - " : " + ")
<< std::fabs(c.im) << "i";
return os;
}
int main() {
Complex p(3, 4), q(1, -2);
std::cout << "p = " << p << "\n";
std::cout << "q = " << q << "\n";
std::cout << "p + q = " << p + q << "\n";
std::cout << "p * q = " << p * q << "\n";
std::cout << "-p = " << -p << "\n";
std::cout << "|p| = " << p.abs() << "\n";
Complex r = p; r += q;
std::cout << "p += q = " << r << "\n";
Complex s(5, 5);
std::cout << "s++ returns " << s++ << ", s is now " << s << "\n";
std::cout << "++s returns " << ++s << ", s is now " << s << "\n";
std::cout << std::boolalpha
<< "p == q ? " << (p == q)
<< " p != q ? " << (p != q) << "\n";
/* 2.0 + p works because operator+ is a FREE function:
2.0 converts to Complex(2.0, 0) via the ctor */
std::cout << "2.0 + p = " << 2.0 + p << "\n";
return 0;
}
Output:
p = 3 + 4i
q = 1 - 2i
p + q = 4 + 2i
p * q = 11 - 2i
-p = -3 - 4i
|p| = 5
p += q = 4 + 2i
s++ returns 5 + 5i, s is now 6 + 5i
++s returns 7 + 5i, s is now 7 + 5i
p == q ? false p != q ? true
2.0 + p = 5 + 4i
2.0 + p compiles. That only works because operator+ is a free function β as a member it would need 2.0.operator+(p), and a double has no members. This is the concrete payoff of the "symmetric operators should be non-members" rule: both operands become eligible for implicit conversion.
#include <iostream>
#include <stdexcept>
#include <cstring>
class IntArray {
int *data;
size_t n;
public:
explicit IntArray(size_t size) : n(size) {
data = new int[n](); /* () zero-fills */
}
IntArray(const IntArray &o) : n(o.n) {
data = new int[n];
std::memcpy(data, o.data, n * sizeof(int));
}
~IntArray() { delete[] data; }
/* ASSIGNMENT β must be a member, must handle self */
IntArray& operator=(const IntArray &o) {
if (this == &o) return *this; /* self-guard */
delete[] data;
n = o.n;
data = new int[n];
std::memcpy(data, o.data, n * sizeof(int));
return *this; /* enables a=b=c */
}
/* SUBSCRIPT β const/non-const pair, with bounds check */
int& operator[](size_t i) {
if (i >= n) throw std::out_of_range("index");
return data[i];
}
const int& operator[](size_t i) const {
if (i >= n) throw std::out_of_range("index");
return data[i];
}
/* CALL operator β makes the object a "functor" */
int operator()(size_t from, size_t to) const {
int sum = 0;
for (size_t i = from; i <= to && i < n; i++) sum += data[i];
return sum;
}
size_t size() const { return n; }
};
std::ostream& operator<<(std::ostream &os, const IntArray &a) {
os << "[";
for (size_t i = 0; i < a.size(); i++)
os << a[i] << (i + 1 < a.size() ? ", " : "");
return os << "]";
}
int main() {
IntArray a(5);
for (size_t i = 0; i < a.size(); i++) a[i] = (i+1) * 10;
std::cout << "a = " << a << "\n";
IntArray b(5), c(5);
c = b = a; /* chained */
b[0] = 999;
std::cout << "b = " << b << " (deep copy: a unchanged)\n";
std::cout << "a = " << a << "\n";
std::cout << "a(1,3) = " << a(1, 3)
<< " (sum of elements 1..3)\n";
a = a; /* self-assign is safe */
std::cout << "after a=a: " << a << "\n";
try { a[99] = 1; }
catch (const std::out_of_range &e) {
std::cout << "a[99] threw: " << e.what() << "\n";
}
return 0;
}
Output:
a = [10, 20, 30, 40, 50]
b = [999, 20, 30, 40, 50] (deep copy: a unchanged)
a = [10, 20, 30, 40, 50]
a(1,3) = 90 (sum of elements 1..3)
after a=a: [10, 20, 30, 40, 50]
a[99] threw: index
#include <iostream>
class Money {
long paisa; /* integer maths, no float error */
public:
Money(long rupees = 0, long p = 0) : paisa(rupees*100 + p) {}
/* the canonical set, each built on the previous */
Money& operator+=(const Money &o) { paisa += o.paisa;
return *this; }
Money& operator-=(const Money &o) { paisa -= o.paisa;
return *this; }
friend Money operator+(Money a, const Money &b) { a += b;
return a; }
friend Money operator-(Money a, const Money &b) { a -= b;
return a; }
friend bool operator==(const Money &a, const Money &b)
{ return a.paisa == b.paisa; }
friend bool operator!=(const Money &a, const Money &b)
{ return !(a == b); }
friend bool operator<(const Money &a, const Money &b)
{ return a.paisa < b.paisa; }
friend bool operator>(const Money &a, const Money &b)
{ return b < a; }
friend bool operator<=(const Money &a, const Money &b)
{ return !(b < a); }
friend bool operator>=(const Money &a, const Money &b)
{ return !(a < b); }
friend std::ostream& operator<<(std::ostream &os,
const Money &m) {
return os << "Rs " << m.paisa / 100
<< "." << (m.paisa % 100 < 10 ? "0" : "")
<< m.paisa % 100;
}
};
int main() {
Money a(150, 75), b(49, 25);
std::cout << a << " + " << b << " = " << a + b << "\n";
std::cout << a << " - " << b << " = " << a - b << "\n";
std::cout << std::boolalpha;
std::cout << "a < b " << (a < b) << " a > b " << (a > b)
<< "\na == b " << (a == b) << " a != b " << (a != b)
<< "\na >= a " << (a >= a) << "\n";
return 0;
}
Output:
Rs 150.75 + Rs 49.25 = Rs 200.00
Rs 150.75 - Rs 49.25 = Rs 101.50
a < b false a > b true
a == b false a != b true
a >= a true
Only +=, -=, == and < contain real logic β the other six operators are one-liners built from those four. That is not laziness; it makes inconsistency structurally impossible. If == is fixed, != is automatically fixed too.
<=>, which collapses the six comparison operators above into one line: auto operator<=>(const Money&) const = default; and the compiler generates all of them consistently. It is one of the most immediately useful features added to the language. In the other direction, look at how Eigen (the standard C++ linear algebra library) uses expression templates so that a + b + c on large matrices allocates no temporaries at all β the operators return lightweight expression objects that fuse into one loop. Search "C++20 spaceship operator" and "expression templates Eigen".:: . .* ?: sizeof) and that precedence, associativity and arity are fixed. Know which must be members (= [] () ->). The prefix versus postfix ++ distinction β the dummy int parameter, and returning by reference versus by value β is asked directly. Writing a complete Complex class with +, *, ==, << and both ++ forms is the standard long question.Create a free account to tick topics off, take notes as you read, watch the video lessons and get a day-by-day study plan built around your exam date.
Loadingβ¦