Object Oriented Programming with C++
9. Operator Overloading
By: Prof. Pandav Patel
Third Semester, August 2020
Computer Engineering Department
Dharmsinh Desai University
Operator Overloading
#include<iostream> Complex addf(Complex &c1, Complex &c2) {
cout << "Inside friend function" << endl;
using std::cout; Complex result; This is a normal function, it is
using std::endl; result.re = c1.re + c2.re; not a method. Hence it needs to
result.im = c1.im + c2.im;
class Complex { return result; be declared as friend function
double re, im; } because it accesses private
public: data memebers
Complex(double re = 0, double im = 0) { int main() {
this->re = re; Complex c1(1.1, 1.1), c2(2.2, 2.2), c3, c4;
this->im = im; c3 = c1.addm(c2);
} c4 = addf(c1, c2);
void print() { // error: no match for ‘operator+’
cout << re << " + j" << im << endl; // (operand types are ‘Complex’ and ‘Complex
} // c3 = c1 + c2; This results in error as +
Complex addm(Complex &c) { c3.print();
cout << "Inside method" << endl; c4.print();
operator has not yet been
Complex result; return 0; overloaded to operate on two
result.re = this->re + c.re; } complex numbers. Hence
result.im = this->im + c.im; compiler does not know which
return result;
} Inside method function or method to call
// declared as friend function because re and im are private Inside friend function
// if they were public, we dont need to declare it as friend 3.3 + j3.3
friend Complex addf(Complex &c1, Complex &c2);
}; 3.3 + j3.3
Operator Overloading
#include<iostream> Complex operator+(Complex &c1, Complex &c2) {
cout << "Inside friend function" << endl;
using std::cout; Complex result;
using std::endl; result.re = c1.re + c2.re;
result.im = c1.im + c2.im;
class Complex { return result;
double re, im; }
public:
Complex(double re = 0, double im = 0) { int main() {
this->re = re; Complex c1(1.1, 1.1), c2(2.2, 2.2), c3, c4;
this->im = im; c3 = c1.addm(c2);
} c4 = c1 + c2;
void print() {
cout << re << " + j" << im << endl;
}
Complex addm(Complex &c) { c3.print();
cout << "Inside method" << endl; c4.print();
Complex result; return 0;
result.re = this->re + c.re; }
result.im = this->im + c.im;
return result;
} Inside method
// declared as friend function because re and im are private Inside friend function
// if they were public, we dont need to declare it as friend 3.3 + j3.3
friend Complex operator+(Complex &c1, Complex &c2);
}; 3.3 + j3.3
Operator Overloading
#include<iostream> Complex operator+(Complex &c1, Complex &c2) {
cout << "Inside friend function" << endl;
using std::cout; Complex result;
using std::endl; result.re = c1.re + c2.re;
result.im = c1.im + c2.im;
class Complex { return result;
double re, im; }
public:
Complex(double re = 0, double im = 0) { int main() {
this->re = re; Complex c1(1.1, 1.1), c2(2.2, 2.2), c3, c4;
this->im = im; c3 = c1 + c2;
} c4 = c1 + c2;
void print() { // Above two operators will result in error
cout << re << " + j" << im << endl; // error: ambiguous overload for ‘operator+’
} // (operand types are ‘Complex’ and ‘Complex’)
Complex operator+(Complex &c) { c3.print();
cout << "Inside method" << endl; c4.print();
Complex result; return 0;
result.re = this->re + c.re; }
result.im = this->im + c.im;
return result;
}
// declared as friend function because re and im are private
// if they were public, we dont need to declare it as friend
friend Complex operator+(Complex &c1, Complex &c2);
};
Operator Overloading
#include<iostream> Complex addf(Complex &c1, Complex &c2) {
cout << "Inside friend function" << endl;
using std::cout; Complex result;
using std::endl; result.re = c1.re + c2.re;
result.im = c1.im + c2.im;
class Complex { return result;
double re, im; }
public:
Complex(double re = 0, double im = 0) { int main() {
this->re = re; Complex c1(1.1, 1.1), c2(2.2, 2.2), c3, c4;
this->im = im; c3 = c1 + c2;
} C4 = addf(c1 ,c2);
void print() {
cout << re << " + j" << im << endl;
}
Complex operator+(Complex &c) { c3.print();
cout << "Inside method" << endl; c4.print();
Complex result; return 0;
result.re = this->re + c.re; }
result.im = this->im + c.im;
return result;
} Inside method
// declared as friend function because re and im are private Inside friend function
// if they were public, we dont need to declare it as friend 3.3 + j3.3
friend Complex addf(Complex &c1, Complex &c2);
}; 3.3 + j3.3
Operator Overloading
New keyword – operator
In order to overload operator, operator function/method needs to be defined
Name of the operator function/method starts with keyword operator, followed by operator
symbol to be overloaded
For example, in order to overload operator ‘+’ to add two complex numbers (c1 + c2)
Complex operator+(Complex &c1, Complex &c2); // overload using function
Complex Complex::operator+(Complex &c); // overload using method
To overload binary operator – function takes two parameters, while method takes one
To overload unary operator – function takes one parameters, while method takes zero
Operator functions are implicit for predefined operators of built-in types and can not be
redefined
Operator functions/methods can be defined for struct and union types as well
Operator functions can be defined for enum type too
At least one argument of an operator function must be of a user-defined type (struct, class,
union, enum), or a reference to one.
Operator Overloading
#include<iostream> Complex operator*(Complex c1, int num) {
cout << "Inside friend function" << endl;
using std::cout; Complex result;
using std::endl; result.re = c1.re * num;
result.im = c1.im * num;
class Complex { return result;
double re, im; }
public:
Complex(double re = 0, double im = 0) { int main() {
this->re = re; Complex c1(1.1, 1.1), c2(2.2, 2.2), c3, c4;
this->im = im; c3 = c1 + c2;
} c4 = c3 * 2;
void print() { c3.print();
cout << re << " + j" << im << endl; c4.print();
} return 0;
Complex operator+(Complex &c) { }
cout << "Inside method" << endl;
Complex result; Inside method
result.re = this->re + c.re;
result.im = this->im + c.im; Inside friend function
return result; 3.3 + j3.3
} 6.6 + j6.6
friend Complex operator*(Complex c1, int num);
};
Operator functions/methods can be defined for two operands of different types too
Parameters can be passed by value or reference to the operator functions/methods
Operator Overloading
Only existing operators could be overloaded. We can not create new operators of our own
(e.g. **, <>)
Intrinsic properties of overloaded operators can not be changed
Preserves arity (number of operands it takes)
Preserves precedence
Preserves associativity
Both unary prefix and postfix could be oveloaded
void class_name::operator++() // prefix
void class_name::operator++(int) // postfix, int is used to distinguish it from prefix
Like overload of other operators, they can also return value and could be overloaded
using functions
Some operators could not be overloaded
:: . .* sizeof ?:
Operators && || and , could be overloaded but they lose their special properties: short-
circuit evaluation and sequencing (they will not be a sequence point for new types)
#include<iostream> Complex Complex::operator*(int num) {
cout << "Inside method II" << endl;
using std::cout; return Complex(this->re * num, this->im * num);
using std::endl; }
class Complex { // This can not be achieved through operator method
double re, im; Complex operator*(int num, Complex c1) {
public: cout << "Inside friend function" << endl;
Complex(double re = 0, double im = 0) { return Complex(c1.re * num, c1.im * num);
this->re = re; }
this->im = im;
} int main() {
void print() { Complex c1(1.1, 1.1), c2(2.2, 2.2), c3, c4, c5;
cout << re << " + j" << im << endl; c3 = c1 + c2;
} c4 = c3 * 2;
Complex operator+(Complex &c) { c5 = 2 * c4;
cout << "Inside method I" << endl; c3.print();
// No need of result object c4.print();
return Complex(this->re + c.re, this->im + c.im); c5.print();
} return 0;
Inside method I
Complex operator*(int num); }
Inside method II
friend Complex operator*(int, Complex);
Inside friend function
};
3.3 + j3.3
6.6 + j6.6
13.2 + j13.2
#include<iostream> Complex Complex::operator*(int num) {
cout << "Inside method II" << endl;
using std::cout; return Complex(this->re * num, this->im * num);
using std::endl; }
class Complex { // This can not be achieved through operator method
double re, im; Complex operator*(int num, Complex c1) {
public: cout << "Inside friend function" << endl;
Complex(double re = 0, double im = 0) { return Complex(c1.re * num, c1.im * num);
this->re = re; }
this->im = im;
} int main() {
void print() { Complex c1(1.1, 1.1), c2(2.2, 2.2), c3, c4, c5;
cout << re << " + j" << im << endl; c3 = c1.operator+(c2); // c1 + c2
} c4 = c3.operator*(2); // c3 * 2
Complex operator+(Complex &c) { c5 = operator*(2, c4); // 2 * c4
cout << "Inside method I" << endl; c3.print();
// No need of result object c4.print();
return Complex(this->re + c.re, this->im + c.im); c5.print();
} return 0;
Inside method I
Complex operator*(int num); }
Inside method II
friend Complex operator*(int, Complex);
Inside friend function
};
3.3 + j3.3
6.6 + j6.6
13.2 + j13.2
#include<iostream> Complex Complex::operator*(int num) {
cout << "Inside method II" << endl;
using std::cout; return Complex(this->re * num, this->im * num);
using std::endl; }
// This can not be achieved through operator method
class Complex { Complex operator*(int num, Complex c1) {
double re, im; cout << "Inside friend function" << endl;
public: return Complex(c1.re * num, c1.im * num);
Complex(double re = 0, double im = 0) { }
this->re = re; int main() {
this->im = im; Complex c1(1.1, 1.1), c2(2.2, 2.2), c3, c4, c5;
} // error: cannot bind non-const lvalue
void print() { // reference of type ‘Complex&’ to an rvalue
cout << re << " + j" << im << endl; // of type ‘Complex’
} // Temporary object can not be assigne to
Complex operator+(Complex &c) { // non-const ref
cout << "Inside method I" << endl; c3 = c1 + c2 * 2;
// No need of result object // No error here as temporary object can
return Complex(this->re + c.re, this->im + c.im); // call method
} c4 = (c1 + c2) * 2;
Complex operator*(int num); c5 = 2 * c1 * 3;
friend Complex operator*(int, Complex); c3.print();
}; c4.print();
c5.print();
return 0;
}
#include<iostream> Complex Complex::operator*(int num) {
cout << "Inside method II" << endl;
using std::cout; return Complex(this->re * num, this->im * num);
using std::endl; }
// This can not be achieved through operator method
class Complex { Complex operator*(int num, Complex c1) {
double re, im; cout << "Inside friend function" << endl;
public: return Complex(c1.re * num, c1.im * num);
Complex(double re = 0, double im = 0) { }
this->re = re; int main() {
this->im = im; Complex c1(1.1, 1.1), c2(2.2, 2.2), c3, c4, c5;
} // error: cannot bind non-const lvalue
void print() { // reference of type ‘Complex&’ to an rvalue
cout << re << " + j" << im << endl; // of type ‘Complex’
} // Temporary object can not be assigne to
Complex operator+(Complex &c) { // non-const ref
cout << "Inside method I" << endl; // c3 = c1 + c2 * 2;
// No need of result object // No error here as temporary object can
return Complex(this->re + c.re, this->im + c.im); // call method
} c4 = (c1 + c2) * 2; Inside method I
Complex operator*(int num); c5 = 2 * c1 * 3; Inside method II
friend Complex operator*(int, Complex); c3.print(); Inside friend function
}; c4.print(); Inside method II
c5.print(); 0 + j0
return 0; 6.6 + j6.6
} 6.6 + j6.6
#include<iostream> Complex Complex::operator*(int num) {
cout << "Inside method II" << endl;
using std::cout; return Complex(this->re * num, this->im * num);
using std::endl; }
// This can not be achieved through operator method
class Complex { Complex operator*(int num, Complex c1) {
double re, im; cout << "Inside friend function" << endl;
public: return Complex(c1.re * num, c1.im * num);
Complex(double re = 0, double im = 0) { }
this->re = re; int main() {
this->im = im; Complex c1(1.1, 1.1), c2(2.2, 2.2), c3, c4, c5;
} // No error, as operator+ accepts arg by value
void print() { c3 = c1 + c2 * 2;
cout << re << " + j" << im << endl; c4 = (c1 + c2) * 2;
} c5 = 2 * c1 * 3;
Complex operator+(Complex c) { c3.print();
cout << "Inside method I" << endl; c4.print(); Inside method II
// No need of result object c5.print(); Inside method I
return Complex(this->re + c.re, this->im + c.im); return 0; Inside method I
} } Inside method II
Complex operator*(int num); Inside friend function
friend Complex operator*(int, Complex); Inside method II
}; 5.5 + j5.5
6.6 + j6.6
6.6 + j6.6
#include<iostream> Complex Complex::operator*(int num) {
cout << "Inside method II" << endl;
using std::cout; return Complex(this->re * num, this->im * num);
using std::endl; }
// This can not be achieved through operator method
class Complex { Complex operator*(int num, Complex c1) {
double re, im; cout << "Inside friend function" << endl;
public: return Complex(c1.re * num, c1.im * num);
Complex(double re = 0, double im = 0) { }
this->re = re; int main() {
this->im = im; Complex c1(1.1, 1.1), c2(2.2, 2.2), c3, c4, c5;
} // No error, as operator+ accepts arg by
void print() { // ref to const
cout << re << " + j" << im << endl; c3 = c1 + c2 * 2;
} c4 = (c1 + c2) * 2;
Complex operator+(const Complex &c) { c5 = 2 * c1 * 3;
cout << "Inside method I" << endl; c3.print(); Inside method II
// No need of result object c4.print(); Inside method I
return Complex(this->re + c.re, this->im + c.im); c5.print(); Inside method I
} return 0; Inside method II
Complex operator*(int num); } Inside friend function
friend Complex operator*(int, Complex); Inside method II
}; 5.5 + j5.5
6.6 + j6.6
6.6 + j6.6
#include<iostream> Complex Complex::operator*(int num) {
cout << "Inside method II" << endl;
using std::cout; return Complex(this->re * num, this->im * num);
using std::endl; }
// Its good practice to pass objects by ref to const
class Complex { Complex operator*(int num, const Complex &c1) {
double re, im; cout << "Inside friend function" << endl;
public: return Complex(c1.re * num, c1.im * num);
Complex(double re = 0, double im = 0) { }
this->re = re; int main() {
this->im = im; Complex c1(1.1, 1.1), c2(2.2, 2.2), c3, c4, c5;
} // No error, as operator+ accepts arg by
void print() { // ref to const
cout << re << " + j" << im << endl; c3 = c1 + c2 * 2;
} c4 = (c1 + c2) * 2;
Complex operator+(const Complex &c) { c5 = 2 * c1 * 3;
cout << "Inside method I" << endl; c3.print(); Inside method II
// No need of result object c4.print(); Inside method I
return Complex(this->re + c.re, this->im + c.im); c5.print(); Inside method I
} return 0; Inside method II
Complex operator*(int num); } Inside friend function
friend Complex operator*(int, const Complex &); Inside method II
}; 5.5 + j5.5
6.6 + j6.6
6.6 + j6.6
#include<iostream> int main() {
Number n1(10), n2(20);
using std::cout; (++++n1).print();
using std::endl; n1.print();
n2++++.print();
class Number { n2.print();
int num; return 0;
public: }
Number(int num) {
this->num = num;
}
Number operator++() {
cout << "Pre-increment:" << num << endl;
num++;
return *this;
}
Number operator++(int) { Pre-increment:10
cout << "Post-increment:" << num << endl; Pre-increment:11
Number temp(*this); 12
num++; 11
return temp; Post-increment:20
} Post-increment:20
void print() { 20
cout << num << endl; 21
}
};
#include<iostream> int main() {
Number n1(10);
using std::cout; cout << "n1 address: " << &n1 << endl;
using std::endl; n1 = ++++n1;
n1.print();
class Number { return 0;
int num; }
public:
Number(int num) {
this->num = num;
}
Number operator++() {
cout << "Pre-increment: " << this << ": ";
cout << num << endl;
num++;
return *this;
}
void print() {
cout << "print function: " << this << ": ";
cout << num << endl;
} n1 address: 0x7fff40a75210
}; Pre-increment: 0x7fff40a75210: 10
Pre-increment: 0x7fff40a75214: 11
print function: 0x7fff40a75210: 12
#include<iostream> int main() {
Number n1(10);
using std::cout; cout << "n1 address: " << &n1 << endl;
using std::endl; ++++n1;
n1.print();
class Number { return 0;
int num; }
public:
Number(int num) {
this->num = num;
}
Number &operator++() {
cout << "Pre-increment: " << this << ": ";
cout << num << endl;
num++;
return *this;
}
void print() {
cout << "print function: " << this << ": ";
cout << num << endl;
} n1 address: 0x7fff92af33d4
}; Pre-increment: 0x7fff92af33d4: 10
Pre-increment: 0x7fff92af33d4: 11
print function: 0x7fff92af33d4: 12
Interesting reads
Operator Overloading
https://en.cppreference.com/w/cpp/language/operators
Why operator function for = operator should return reference of compatible
type
https://stackoverflow.com/questions/42335200/assignment-operator-
overloading-returning-void-versus-returning-reference-param
Why binding temporary object to local non-const lvalue reference is not
allowed while calling of non-const member function by temporary object is
allowed
https://stackoverflow.com/questions/51338287/c-whats-the-design-
philosophy-of-allowing-temporary-object-to-call-non-const