Object-Oriented
Programming
Mohammad Salman
Dean’s Fellow
Operator Overloading
[Link]
Idea Behind Operators
• Operators are essentially #include<iostream>
functions that the compiler int main()
calls for us. {
int x { 2 };
int y { 3 };
• In this example, the function // x + y equivalent to operator+(x,
being called is, y)
std::cout << x + y << '\n';
• operator+(int, int)
return 0;
}
Idea Behind Operators
• In this example, doubles are #include<iostream>
being added. int main()
{
double x { 2.1 };
• The compiler calls the relevant double y { 3.8 };
overload // x + y equivalent to operator+(x,
• operator+(double, y)
std::cout << x + y << '\n';
double)
return 0;
}
In both these examples, we were working with
built-in data types.
The following doesn’t work.
Fraction isn’t a built-in data type; the compiler doesn’t know how to add these two objects together.
class Fraction int main()
{ {
private: Fraction f1{ 1, 4 }; // f1 = 1/4
int m_num; Fraction f2{ 2, 4 }; // f2 = 2/4
int m_den;
std::cout << f1 + f2 << '\n'; //
public: 3/4
Fraction(int num, int den)
: m_num{ num }, m_den{ den } return 0;
{} }
void show() const
{ std::cout << m_num << '/' << m_den << '\
n'; }
};
Btw, new and delete are also operators that can be overloaded.
You can even overload the subscript operator (operator[]).
So when we talk about operator overloading, don’t restrict yourselves to just arithmetic operators.
Writing Operator Overloads
Using Normal Functions
These functions will not be part of your class.
[Link]
// This function is NOT part of the class (not a method).
Fraction operator*(const Fraction& frac1, const Fraction&
Because this function isn’t a frac2)
member function, I cannot {
int resultNum{ [Link]() * [Link]() };
access private members of int resultDen{ [Link]() * [Link]() };
Fraction-type objects
inside this function. Fraction result{ resultNum, resultDen };
return result;
}
class Fraction
{
private: int main()
int m_num; {
int m_den;
Fraction f1{ 1, 4 }; // f1 =
public:
1/4
Fraction(int num, int den) Fraction f2{ 2, 4 }; // f2 =
: m_num{ num }, m_den{ den } 2/4
{}
Fraction fracProd{ f1 * f2 };
void show() const
{ std::cout << m_num << '/' << m_den << '\ [Link](); // 2/16
n'; }
int getNum() const { return m_num; } return 0;
int getDen() const { return m_den; } }
};
Note the following.
• The name of this function.
// Within main()
• The parameters of this function. Fraction fracProd{ f1 *
f2 };
• The return type of this function.
• The implementation logic is based on how fractions get multiplied mathematically.
Fraction operator*(const Fraction& frac1, const Fraction&
frac2)
{
int resultNum{ [Link]() * [Link]() };
int resultDen{ [Link]() * [Link]() };
Fraction result{ resultNum, resultDen };
return result;
}
// This function is NOT part of the class (not a method).
Fraction operator*(const Fraction& frac1, int val)
{
int resultNum{ [Link]() * val };
Similarly, you can also overload int resultDen{ [Link]() };
the function in the following
Fraction result{ resultNum, resultDen };
way.
return result;
}
class Fraction
{
private: int main()
int m_num; {
int m_den;
Fraction f1{ 1, 2 }; // f1 =
public:
1/2
Fraction(int num, int den)
: m_num{ num }, m_den{ den } Fraction fracProdInt{ f1 * 3 };
{}
[Link]();
void show() const
{ std::cout << m_num << '/' << m_den << '\ return 0;
n'; }
}
int getNum() const { return m_num; }
int getDen() const { return m_den; }
};
Fraction operator*(const Fraction& frac1, int val)
{
int resultNum{ [Link]() * val };
int resultDen{ [Link]() };
Fraction result{ resultNum, resultDen };
return result;
}
Will this also work for the following.
Fraction fracProdInt{ 3 * f1 };
No! Note the order of the parameters in the definition above.
This is the error that comes up.
Do note that,
• The first operand becomes the first parameter and,
• The second operand becomes the second parameter. // Within main()
Fraction fracProd{ f1 *
This applies to binary operators (operators that work with f2 };
two operands).
Fraction operator*(const Fraction& frac1, const Fraction&
frac2)
{
int resultNum{ [Link]() * [Link]() };
int resultDen{ [Link]() * [Link]() };
Fraction result{ resultNum, resultDen };
return result;
}
Overloading the I/O
Operators
Insertion Operator (<<) and the Extraction Operator (>>)
The following discussion is fully inspired from the reference link provided below.
Please go read.
[Link]
std::cout (contd.)
Figure 2.2 from OOP in C++ by Robert
Lafore.
17
std::cin (contd.)
Figure 2.5 from OOP in C++ by Robert
Lafore.
18
Let’s take a look at a new class for this discussion – class Point.
class Point
{
private:
double m_x;
double m_y;
double m_z;
public:
Point(double x=0.0, double y=0.0, double
z=0.0)
: m_x{x}, m_y{y}, m_z{z}
{
}
double getX() const { return m_x; }
double getY() const { return m_y; }
double getZ() const { return m_z; }
};
If you wanted to print the attributes of a Point-type object, you would have to rely on the getters.
This could become tedious.
Point point{5.0, 6.0, 7.0};
class Point std::cout << "Point(" << [Link]() << ",
{ "
private: << [Link]() << ", "
double m_x;
double m_y;
<< [Link]() << ')';
double m_z;
public:
Point(double x=0.0, double y=0.0, double
z=0.0)
: m_x{x}, m_y{y}, m_z{z}
{
}
double getX() const { return m_x; }
double getY() const { return m_y; }
double getZ() const { return m_z; }
};
We can make the previous task easier by creating a show() or print() method like we’ve done
before.
class Point
{
private:
double m_x{};
double m_y{};
double m_z{};
public:
Point(double x=0.0, double y=0.0, double z=0.0)
: m_x{x}, m_y{y}, m_z{z}
{
}
double getX() const { return m_x; }
double getY() const { return m_y; }
double getZ() const { return m_z; }
void print() const
{
std::cout << "Point(" << m_x << ", " << m_y << ", " << m_z <<
')';
}
};
This also has some problems. You will have to do something like this (recall your exam question).
print() returns void, so you cannot cascade this function call with the other print statements.
class Point int main()
{
private:
{
double m_x{}; Point point{5.0, 6.0, 7.0};
double m_y{};
double m_z{}; std::cout << "My point is: ";
public: [Link]();
Point(double x=0.0, double y=0.0, double z=0.0) std::cout << " in Cartesian space.\
: m_x{x}, m_y{y}, m_z{z} n";
{
}
return 0;
double getX() const { return m_x; } }
double getY() const { return m_y; }
double getZ() const { return m_z; }
void print() const
{
std::cout << "Point(" << m_x << ", " << m_y << ", " << m_z <<
')';
}
};
It could be a lot better if you could actually do something like this.
int main()
{
Point point{5.0, 6.0, 7.0};
std::cout << "My point is: " << point << " in Cartesian space.\
n";
return 0;
}
Currently, we can’t do this, because the compiler doesn’t know how to print Point-type objects.
More precisely, the compiler doesn’t have the function that could print Point-type objects.
OPERATOR OVERLOADING is the answer.
But which operator needs to be overloaded?
We need to overload the insertion operator (<<).
std::cout << point; // dtype of “point” is Point
<< is a binary operator – two operands.
Left operand: an object of type (or class) std::ostream.
Right operand: an object of type (or class) Point
Based on all this information, we have some information about the function that we have to overload.
Name: operator<<
Parameters: (std::ostream& out, const Point& pt) // const b/c I just want to print
Return type: ???
Understanding the return type of this function requires some thinking.
Let’s first see what the implementation of the function looks like to get some ideas.
Basically, I’m just printing the attributes of the Point object.
??? operator<< (std::ostream& out, const Point& point)
{
out << "Point(" << [Link]() << ", " << [Link]() << ", " << [Link]() <<
')';
}
This will print the attributes on the screen. We could set it to void.
What if we do this in main()?
class Point
{
private:
double m_x;
double m_y; int main()
double m_z; {
Point point1{2.0, 3.0,
public:
4.0};
Point(double x=0.0, double y=0.0, double
z=0.0)
: m_x{x}, m_y{y}, m_z{z} std::cout << point1;
{}
return 0;
double getX() const { return m_x; } }
double getY() const { return m_y; }
double getZ() const { return m_z; }
};
void operator<< (std::ostream& out, const Point& point)
{
out << "Point(" << [Link]() << ", " << [Link]() << ", " << [Link]() <<
')';
}
std::cout << point1 << '\n'; Doesn’t make sense.
Hence, return type
void << '\n'; cannot be void.
class Point
{
private:
double m_x;
double m_y; int main()
double m_z; {
Point point1{2.0, 3.0,
public:
4.0};
Point(double x=0.0, double y=0.0, double
z=0.0)
: m_x{x}, m_y{y}, m_z{z} std::cout << point1 << '\
{} n';
double getX() const { return m_x; } return 0;
double getY() const { return m_y; } }
double getZ() const { return m_z; }
};
std::ostream& operator<< (std::ostream& out, const Point& point)
{
out << "Point(" << [Link]() << ", " << [Link]() << ", " << [Link]() <<
')';
return out;
}
std::cout << point1 << '\n'; What we need to be
returned is cout itself.
std::cout << '\n'; And so we can return by
reference.
class Point
{
private:
double m_x;
double m_y; int main()
double m_z; {
Point point1{2.0, 3.0,
public:
4.0};
Point(double x=0.0, double y=0.0, double
z=0.0)
: m_x{x}, m_y{y}, m_z{z} std::cout << point1 << '\
{} n';
double getX() const { return m_x; } return 0;
double getY() const { return m_y; } }
double getZ() const { return m_z; }
};
std::ostream& operator<< (std::ostream& out, const Point& point)
{
out << "Point(" << [Link]() << ", " << [Link]() << ", " << [Link]() <<
')';
return out;
}
class Point
{
private:
double m_x;
double m_y; int main()
double m_z; {
Point point1{2.0, 3.0,
public:
4.0};
Point(double x=0.0, double y=0.0, double
z=0.0)
: m_x{x}, m_y{y}, m_z{z} std::cout << point1 << '\
{} n';
double getX() const { return m_x; } return 0;
double getY() const { return m_y; } }
double getZ() const { return m_z; }
};
This is the explanation of what we talked about in class.
Implementation of the extraction operator (>>) works in a similar way.
std::cin >> point; // dtype of “point” is Point
>> is a binary operator – two operands.
Left operand: an object of type (or class) std::istream.
Right operand: an object of type (or class) Point
Note that parameter point must be
non-const so we can modify the class
Based on this, this is what our function would look like. members with the input values.
std::istream& operator>> (std::istream& in, Point&
point)
{
in >> point.m_x;
in >> point.m_y;
in >> point.m_z;
return in;
}
Another thing to note is that the members are private yet I’m accessing them outside the class.
The reason this works is that I can make this function a friend of my class.
class Point
{
private:
double m_x{};
double m_y{}; Similarly, you can also make operator<<
double m_z{}; a friend of your class as well and then it
could also access the private members.
public:
Point(double x=0.0, double y=0.0, double z=0.0)
: m_x{x}, m_y{y}, m_z{z}
{}
friend std::istream& operator>> (std::istream& in, Point&
point);
};
std::istream& operator>> (std::istream& in, Point& point)
{
in >> point.m_x;
Alternatively, you could have
in >> point.m_y;
used public setter methods in >> point.m_z;
from the Point class if not
using friend functions. return in;
}
(SS) The compiler allows putting the definition of a friend function inside the class like so. However, this function is
still considered a non-member function. Recall that when defining member functions, friend is not required.
class Point
{
private:
double m_x{};
double m_y{};
double m_z{};
public:
Point(double x=0.0, double y=0.0, double z=0.0)
: m_x{x}, m_y{y}, m_z{z}
{}
friend std::istream& operator>> (std::istream& in, Point& point)
{
in >> point.m_x;
in >> point.m_y;
in >> point.m_z;
return in;
}
};