Operator overloading

Operator overloading

Contents

Objectives

Types of operators

Arithmetic operators

Increment/descrement

Inserter/extractor operators

Assignment operator

Index operator

Relational and equality operators

Conversion operators

Operator overloading

Objective

To make the class usage easier, more intuitive

The ability to read an object using the extractor operator (>>)

Employee e1;
cin >> e1;

The ability to write an object using the inserter operator (<<)

Employee e2;
cout << e << endl;

The ability to compare objects of a given class

cout << ((e1 < e2) ? "lest" : "greater")

Operator overloading: a service to the clients of the class

Classes and Objects

Limitations

You cannot add new operator symbols. Only the exitsting operators can be redefined

Some operators cannot be overloaded:

.(member access in an object)

::(scope resolution operator)

sizeof

?:

You cannot change the arity(the number of arguments) of the opertor

You cannot change the precedence or associativity of the operator

Classes and Objects

How to implement?

Write a function with the name operator <symbol>

Alternatives:

Method of you class

Global function (usually a freind of the class)

Classes and Objects

There are 3 types of operators

Operators that must be methods (member functions)

they don't make sense outside of a class

operator=, operator(), operator[], operator->

Operators that must be global functions

The left-hand side of the operator is a variable of different type than you class:

operator <<, operator >>

cout << emp;
	cout: ostream;
	emp: Employee;
			

Operators that can be either methods or global functions

Gregoire: "Make every operator a method unless you must make it a global function"

Classes and Objects

Choosing argument types

Value vs. reference

Prefer passing-by-reference instead of passing-by-value

const vs. non const

Prefer const unless you modify it.

Choosing return types

You can spectify any return a type, however

follow the built-in values rule

comparation always return bool

arithmetic operator return an object representing the result of the arithmetic

Operator overloading

The Complex class:

class Complext{
	double real, image;
public:
	Complex(double, double);
	void setReal(double);
	void setImage(double);
	double getReal() const;
	double getImage() const;
	void print() const;
}
				
Complex::Complex(double real, double image):
	real(real), image(image){}
void Complex::setReal(double real){
	this->real = real;
}
void Complex::setImage(double image){
	this->image = image;
}
double Complex::getReal()const{
	return real;
}
double Complex::getImage()const{
	return image;
}
void Complex::print()const{
	cout << real << image;
}
				
Operator overloading

Arithmetic operators (member or standalone function)

unary minus

binary minus

Complex Complex::operator-() const{
	Complex temp(-real, - image);
	return temp;
}
Complex Complex::operator-(const Complex& c)const{
	Complex temp(real - c.real, image - c.image);
	return temp;
}
			
Operator overloading

Arithmetic operators (member or standalone function)

unary minus

binary minus

Complex operator-(const Complex& c) const{
	Complex temp(-c.getReal(), - c.getImage());
	return temp;
}
Complex operator-(const Complex& c1, const Complex& c2)const{
	Complex temp(c1.getReal() - c2.getReal(), c1.getImage() - c2.getImage());
	return temp;
}
			
Operator overloading

Increment/Decrement operators

Postincrement

int i = 10;
int j = i++; //j -> 10
			

Preincrement

int i = 10;
int j = ++i; //j -> 11
			

The C++ standard specifies that the prefix increment and decrement return an lvalue (left value)

Operator overloading

Increment/Decrement operators (member function)

//prefix
Complex& Complex::operator++(){
	real++;
	image++;
	return *this;
}
//postfix
Complex Complex::operator++(int){
	Complex temp(*this);
	real++;
	image++;
	return temp;
}
			

Which one is more efficient?

Why?

Operator overloading

Inserter/Extractor operator (standalone function)

//Complex.h
class Complex{
public:
	friend ostream& operator << (ostream& os, const Complex& c);
	friend istream& operator >> (istream& is, Complex& c);
	//...
}
//Complex.cpp
ostream& operator << (ostream& os, const Complext& c){
	os << c.real << "+" << c.image << "i";
	return os;
}
istream& operator >> (istream& is, Complex& c){
	is >> c.real >> c.image;
	return is;
}
			
Operator overloading

Inserter/Extractor operator

Syntax

ostream& operator << (ostream& os, const T& out)

istream& operator >> (istream& is, T& in)

Remark

Streams are always passed by reference

Q: Why should inserter operator return an ostream& ?

Q: Why should extractor operator retur nan istram& ?

Operator overloading

Inserter/Extractor operators

Usage:

Complex z1, z2;
cout << "Read 2 complex number: ";
//Extractor
cin >> z1 >> z2;
//Inserter
cout << "z1: " << z1 << endl;
cout << "z2: " << z2 << endl;
			
Operator overloading

Assignment operator

Q: When should be overloaded?

Q: When bitwise copy is not satisfactory (if you hava dynamically allocated memory)=>

When we should implement the copy constructor and destructor too

Ex: out Stack class

Operator overloading

Copy assignment operator(member function)

Systax

X& operator= (const X& obj)

Q: is the return type necessary?

Analyze the following example code

Complex z1(1, 2), z2(2, 3), z3(1, 1);
z3 = z1;
z2 = z1 = z3;
			
Operator overloading

Copy assignment operator example

Stack& Stack::operator=(const Stack& s){
	if(this != &s){
		delete[] elments;
		capacity = 0;
		elements = NULL;
		capacity = s.capacity;
		element = new double[capacity];
		int n = s.top - s.elements;
		std::copy(s.elements, s.elements + n, elements);
		top = elements + n;
	}
	return *this;
}
			
Operator overloading

Copy assignment operator vs Copy constructor

Complex z1(1, 2), z2(2, 4); //constructor
Complex z3 = z3; //Copy constructor
Complex z4(z2); //Copy constructor
z1 = z2; //Copy assignment operator
			
Operator overloading

Subcript operator: neaded for arrays (member function)

Suppose you want your own dynamically allocated C-style array => implement your own Array

class Array{
	double elements;
	int size;
public:
	Array(int size = 0);
	~Array();
	Array(const Array&);
	Array& operator=(const Array&);
	double& operator[](int index);
	double operator[](int index)const;//read-only access
}
			
Operator overloading

Implement

Array::Array(int size){
	if(size < 0){
		size = 10;
	}
	this->size = size;
	elements = new double[size];
}
Array::~Array(){
	if(elements != NULL){
		delete[] elements;
		elements = NULL;
	}
}
				
double& Array::operator[](int index){
	if(index < 0 || index >= size){
		throw out_of_range("out of range");
	}
	return elements[index];
}
double Array::operator[](int index)const{
	if(index < 0 || index >= size){
		throw out_of_range("out of range");
	}
	return elements[index];
}
				
Operator overloading

const vs non-const [] operator

void print(const Array& arr, int size){
	for(int i = 0; i < size; i++){
		cout << arr[i];
	}
	cout << endl;
}
			
Array arr;
for(int i = 0; i < 10; i++){
	arr[i] = 100;
}
print(arr, 10);
			
Operator overloading

Relational and equality operators

Used for search and sort

The container must be able to compare the stored objects

bool operator== (const Point& p1, const Point &p2){
	return p1.getX() == p2.getX() && p1.getY() == p2.getY();
}
bool operator< (const Point& p1, const& p2){
	return p1.distance(Point(0, 0)) < p2.distance(Point(0 , 0));
}