0% found this document useful (0 votes)
9 views

L02c OperatorOverloading

Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
9 views

L02c OperatorOverloading

Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 27

1

CSCI 104
Operator Overloading

Mark Redekopp

© 2022 by Mark Redekopp. This content is protected and may not be shared, uploaded, or distributed.
Revised: 05/2021
2

OPERATOR OVERLOADING REVIEW

© 2022 by Mark Redekopp. This content is protected and may not be shared, uploaded, or distributed.
3

Operator Overloading Review


Member or Non-member? Arguments
• How do you decide if you can • For member function
make the operator overload operator overloads, how
function a member function many input arguments are
of the class? needed for operator+? For
• When do you have to use a operator! ?
non-member operator
function? // arbitrary precision integer class
class BigInt {
// arbitrary precision integer class ____ operator+( );
class BigInt { ____ operator!( );
... };
}; int main(){
int main(){ BigInt w, x, y, z;
BigInt x, y, z; w = x + y;
x = y + 5; bool flag = !w;
} }
© 2022 by Mark Redekopp. This content is protected and may not be shared, uploaded, or distributed.
4

Operator Overloading Review


Return types Chaining
• For class BigInt which • Do we need operator
models an arbitrary overload functions with 2-,
precision integer, what 3-, 4-inputs, etc. to handle
should the return type be various use cases?
for:
– Operator+
– Operator==
class BigInt { class BigInt {
public: ...
_________ operator+(const BigInt&); };
_________ operator==(const BigInt&); int main(){
}; BigInt w, x, y, z;
int main(){ w = x + y + z;
BigInt w, x, y, z; cout << w << " is a bigint!" << endl;
w = x + y; }
}
© 2022 by Mark Redekopp. This content is protected and may not be shared, uploaded, or distributed.
5

PRE-SUMMER 2021 SLIDES

© 2022 by Mark Redekopp. This content is protected and may not be shared, uploaded, or distributed.
6

Function Overloading
• What makes up a signature (uniqueness) of a function
– name
– number and type of arguments
• No two functions are allowed to have the same signature; the
following 5 functions are unique and allowable…
– void f1(int); void f1(double, int);
– void f1(double); void f1(int, int);
• We say that “f1” is overloaded 5 times
• Notes:
• Return type does NOT make signature unique
– int f1(); is considered the same as void f1();
• For member functions, 'const' make signature unique
– int& List::get() int const & List::get() const;
© 2022 by Mark Redekopp. This content is protected and may not be shared, uploaded, or distributed.
7

Operator Overloading
class User{
public:
User(string n); // Constructor
• C/C++ defines operators (+,*,-,==,etc.) that work string get_name();
with basic data types like int, char, double, etc. private:

user.h
int id_;
• C/C++ has no clue what classes we’ll define and string name_;
what those operators would mean for these yet- };
to-be-defined classes
#include “user.h”
– class complex { User::User(string n) {
public: name_ = n;
double real, imaginary;

user.cpp
}
}; string User::get_name(){
return name_;
– Complex c1,c2,c3; }
// should add component-wise
c3 = c1 + c2; #include<iostream>
– class List { #include “user.h”
... int main(int argc, char *argv[]) {
}; User u1(“Bill”), u2(“Jane”);

user_test.cpp
– List l1,l2; // see if same username
l1 = l1 + l2; // should concatenate // Option 1:
if(u1 == u2) cout << “Same”;
// l2 items to l1
// Option 2:
• We can write custom functions to tell the if(u1.get_name() == u2.get_name())
compiler what to do when we use these { cout << “Same” << endl; }
operators! Let us learn how… return 0:
© 2022 by Mark Redekopp. This content is protected and may not be shared, uploaded, or distributed. }
8

Two Approaches
• There are two ways to specify an operator
overload function
– Global level function (not a member of any class)
– As a member function of the class on which it will
operate
• Which should we choose?
– It depends on the left-hand side operand (e.g.
string + int or iostream + Complex )

© 2022 by Mark Redekopp. This content is protected and may not be shared, uploaded, or distributed.
9

Method 1: Global Functions


• Can define global functions int main()
{
with name "operator{+-…}" int hour = 9;
string suffix = "p.m.";
taking two arguments string time = hour + suffix;
– LHS = Left Hand side is 1st arg // WON'T COMPILE…doesn't know how to
// add an int and a string
– RTH = Right Hand side is 2nd arg }
return 0;

• When compiler encounters an string operator+(int time, string suf)


{
operator with objects of stringstream ss;
ss << time << suf;
specific types it will look for an return ss.str();
}
"operator" function to match int main()
{
and call it int hour = 9;
string suffix = "p.m.";
• But what if we need to access
private data of some object to string time = hour + suffix;
// WILL COMPILE TO:
implement our operation? // string time = operator+(hour, suffix);

– A global (non-member) function return 0;


}
will This
© 2022 by Mark Redekopp. not work.
content is protectedWe need
and may method
not be shared, 2
uploaded, or distributed.
10

Method 2: Class Members


class Complex
{ public:
Complex();
• C++ allows users to write class Complex(double r, double i);
Complex operator+(const Complex &rhs) const;
member functions that define
what an operator should do for a private:
double real, imag;
class };

• Same naming convention: Complex Complex::operator+(const Complex &rhs) const


{
function name starts with Complex temp;
‘operator’ and then the actual temp.real = real + rhs.real;
temp.imag = imag + rhs.imag;
operator return temp;
}
• Important: Left-hand side is the int main()
{
implied calling object for which Complex c1(2,3);
the member function is called and Complex c2(4,5);
Complex c3 = c1 + c2;
Right-hand side is passed as the // Same as c3 = c1.operator+(c2);
cout << c3.real << "," << c3.imag << endl;
argument // can overload '<<' so we can write:
– LHS-arg.operator+(RHS-arg); // cout << c3 << endl;
return 0;
}

© 2022 by Mark Redekopp. This content is protected and may not be shared, uploaded, or distributed.
11

Overloading Notes
• You can overload any operator except the member
operator (.), the scope operator (::), and the ternary
operator ( ? : )
– Binary operators: +, -, *, /, ++, --
– Comparison operators: ==, !=, <, >, <=, >=
– Assignment: =, +=, -=, *=, /=, etc.
– I/O stream operators: <<, >>
• You cannot change the operators precedence
– Multiply must always come before addition
• More questions: https://isocpp.org/wiki/faq/operator-overloading

© 2022 by Mark Redekopp. This content is protected and may not be shared, uploaded, or distributed.
12

Binary Operator Overloading


• For binary operators, do the operation on a new object's data
members and return that object
– Don’t want to affect the input operands data members
• Difference between: x = y + z; vs. x = x + z;
• Normal order of operations and associativity apply (can’t be
changed)
• Can overload each operator with various RHS types…
– See next slide

© 2022 by Mark Redekopp. This content is protected and may not be shared, uploaded, or distributed.
13

Binary Operator Overloading


class Complex
{
No special code is needed to add 3 or more
public: operands. The compiler chains multiple calls to
Complex(); the binary operator in sequence.
Complex(double r, double i);
Complex operator+(const Complex &rhs) const;
Complex operator+(int real) const;
private: int main()
double real, imag; {
}; Complex c1(2,3), c2(4,5), c3(6,7);
Complex Complex::operator+(const Complex &rhs) const
{ Complex c4 = c1 + c2 + c3;
Complex temp; // (c1 + c2) + c3
temp.real = real + rhs.real; // c4 = c1.operator+(c2).operator+(c3)
temp.imag = imag + rhs.imag; // = anonymous-ret-val.operator+(c3)
return temp;
}
c3 = c1 + c2;
Complex Complex::operator+( int real ) const c3 = c3 + 5;
{
Complex temp = *this; }
temp.real += real;
return temp;
} Adding different types
(Complex + Complex vs.
Complex + int) requires
different overloads
© 2022 by Mark Redekopp. This content is protected and may not be shared, uploaded, or distributed.
14

Relational Operator Overloading


• Can overload class Complex
{
==, !=, <, <=, >, >= public:
Complex();
• Should return bool Complex(double r, double i);
Complex operator+(const Complex &rhs) const;
bool operator==(const Complex &rhs) const;
double real, imag;
};

bool Complex::operator==(const Complex &rhs) const


{
return (real == rhs.real && imag == rhs.imag);
}

int main()
{
Complex c1(2,3);
Complex c2(4,5);
// equiv. to c1.operator==(c2);
if(c1 == c2)
cout << “C1 & C2 are equal!” << endl;

return 0;
}

Nothing will be displayed


© 2022 by Mark Redekopp. This content is protected and may not be shared, uploaded, or distributed.
15

Practice On Own
• In the online exercises, add the following
operators to your Str class
– operator[]
– operator==(const Str& rhs);
– If time do these as well but if you test them they
may not work…more on this later!
– operator+(const Str& rhs);
– operator+(const char* rhs);

© 2022 by Mark Redekopp. This content is protected and may not be shared, uploaded, or distributed.
16

Non-Member Functions
int main()
• What if the user changes the {
Complex c1(2,3);
order? Complex c2(4,5);
Complex c3 = 5 + c1;
– int on LHS & Complex on RHS // ?? 5.operator+(c1) ??
– No match to a member function // ?? int.operator+(c1) ??
// there is no int class we can
b/c to call a member function // change or write
the LHS has to be an instance of
that class return 0;
}
• We can define a non- Doesn't work without a new operator+ overload
member function (global Complex operator+(const int& lhs, const Complex &rhs)
scope function) that takes in {
Complex temp;
two parameters (both the temp.real = lhs + rhs.real; temp.imag = rhs.imag;
return temp;
LHS & RHS) }
– May need to declare it as a int main()
{
friend Complex c1(2,3);
Complex c2(4,5);
Complex c3 = 5 + c1; // Calls operator+(5,c1)
return 0;
}
Still a problem with this code
Can operator+(…) access Complex's private data?
© 2022 by Mark Redekopp. This content is protected and may not be shared, uploaded, or distributed.
17

Friend Functions
• A friend function is a class Silly
{
function that is not a public:
member of the class but Silly(int d) { dat = d };
friend int inc_my_data(Silly &s);
has access to the private private:
int dat;
data members of instances };
of that class
// don't put Silly:: in front of inc_my_data(...)
• Put keyword ‘friend’ in // since it isn't a member of Silly
int inc_my_data(Silly &a)
function prototype in class {
Notice inc_my_data is NOT a
s.dat++;
definition return s.dat; member function of Silly. It's a
• Don’t add scope to } global scope function but it
now can access the private
function definition int main()
class members.
{
Silly cat(5);
//cat.dat = 8
// WON'T COMPILE since dat is private

int x = inc_my_data(cat);
cout << x << endl;
}

© 2022 by Mark Redekopp. This content is protected and may not be shared, uploaded, or distributed.
18

Non-Member Functions
class Complex
• Revisiting the previous {
public:
problem Complex();
Complex(double r, double i);
// this is not a member function
friend Complex operator+(const int&, const Complex& );
private:
double real, imag;
};

Complex operator+(const int& lhs, const Complex &rhs)


{
Complex temp;
temp.real = lhs + rhs.real; temp.imag = rhs.imag;
return temp;
}
int main()
{
Complex c1(2,3);
Complex c2(4,5);
Complex c3 = 5 + c1; // Calls operator+(5,c1)
return 0;
}

Now things work!


© 2022 by Mark Redekopp. This content is protected and may not be shared, uploaded, or distributed.
19

Why Friend Functions?


• Can I do the following? class Complex
• error: no match for 'operator<<' in 'std::cout << c1' {
• /usr/include/c++/4.4/ostream:108: note:
public:
candidates are: /usr/include/c++/4.4/ostream:165: Complex();
note: std::basic_ostream<_CharT, Complex(double r, double i);
_Traits>& std::basic_ostream<_CharT, Complex operator+(const Complex &rhs) const;
_Traits>::operator<<(long int) [with _CharT = char, private:
_Traits = std::char_traits<char>] double real, imag;
• /usr/include/c++/4.4/ostream:169: note: };
std::basic_ostream<_CharT, _Traits>&
std::basic_ostream<_CharT,
_Traits>::operator<<(long unsigned int) [with int main()
_CharT = char, _Traits = std::char_traits<char>] {
• /usr/include/c++/4.4/ostream:173: note: Complex c1(2,3);
std::basic_ostream<_CharT, _Traits>& cout << c1; // equiv. to cout.operator<<(c1);
std::basic_ostream<_CharT, cout << endl;
_Traits>::operator<<(bool) [with _CharT = char, return 0;
_Traits = std::char_traits<char>]
}
• /usr/include/c++/4.4/bits/ostream.tcc:91: note:
std::basic_ostream<_CharT, _Traits>&
std::basic_ostream<_CharT,
_Traits>::operator<<(short int) [with _CharT = char,
_Traits = std::char_traits<char>]

© 2022 by Mark Redekopp. This content is protected and may not be shared, uploaded, or distributed.
20

Why Friend Functions?


• cout is an object of type ‘ostream’ class Complex
{
• << is just an operator public:
Complex();
• But we call it with ‘cout’ on the Complex(double r, double i);
Complex operator+(const Complex &rhs) const;
LHS which would make private:
“operator<<“ a member function double real, imag;
};
of class ostream
• Ostream class can’t define these int main()
{
member functions to print out Complex c1(2,3);
user defined classes because they cout << “c1 = “ << c1;
// cout.operator<<(“c1 = “).operator<<(c1);
haven’t been created
// ostream::operator<<(char *str);
• Similarly, ostream class doesn’t // ostream::operator<<(Complex &src);
have access to private members
cout << endl;
of Complex return 0;
}

© 2022 by Mark Redekopp. This content is protected and may not be shared, uploaded, or distributed.
21

Ostream Overloading
class Complex
• Can define operator {
public:
functions as friend Complex();
Complex(double r, double i);
functions Complex operator+(const Complex &rhs) const;
friend ostream& operator<<(ostream&, const Complex &c);
• LHS is 1st arg. private:
int real, imag;
• RHS is 2nd arg. };

• Use friend function so LHS ostream& operator<<(ostream &os, const Complex &c)
{
can be different type but os << c.real << “,“ << c.imag << “j”;
still access private data //cout.operater<<(c.real).operator<<(“,”).operator<<...
return os;
• Return the ostream& (i.e. }

os which is really cout) so int main()


{
you can chain calls to '<<' Complex c1(2,3), c2(4,5);
and because cout/os object cout << c1 << c2;
// operator<<( operator<<(cout, c1), c2);
has changed cout << endl;
return 0;
}

Template for adding ostream capabilities:


friend ostream& operator<<(ostream &os, const T &rhs);
(where T is your user defined type)
© 2022 by Mark Redekopp. This content is protected and may not be shared, uploaded, or distributed.
22

Implicit Type Conversion


• Would the following if condition class Student {
private: int id; double gpa;
make sense? };
int main()
• No! If statements want Boolean {
variables Student s1;
if(s1){ cout << "Hi" << endl; }
return 0;
}

• But you've done things like this ifstream ifile(filename);


...
before while( ifile >> x )
– Operator>> returns an ifstream& { ... }

• So how does ifstream do it?


class Student {
– With an "implicit type conversion
private:
operator overload" int id; double gpa;
– Student::operator bool() public:
• Code to specify how to convert a operator bool() { return gpa>= 2.0;}
Student to a bool operator int() { return id; }
};
– Student::operator int()
• Code to specify how to convert a Student s1;
Student to an int if(s1) // calls operator bool() and
int x = s1; // calls operator int()
© 2022 by Mark Redekopp. This content is protected and may not be shared, uploaded, or distributed.
23

Member or Friend?
Should I make my operator overload be a member of a class, C1?

Ask yourself: Is the LHS an instance of C1?


YES NO

C1 objA; C1 objA;
objA << objB // or objB << objA // or
objA + int int + objA
YES the operator overload function NO the operator overload function should
can be a member function of the C1 be a global level (maybe friend) function
class since it will be translate to such as operator<<(cout, objA). It cannot
objA.operator<<(…) be a member function since it will be
translate to objB.operator<<(…).

© 2022 by Mark Redekopp. This content is protected and may not be shared, uploaded, or distributed.
24

Summary
• If the left hand side of the operator is an instance of that class
– Make the operator a member function of a class…
– The member function should only take in one argument which is the RHS
object
• If the left hand side of the operator is an instance of a different
class
– Make the operator a friend function of a class…
– This function requires two arguments, first is the LHS object and second is
the RHS object

© 2022 by Mark Redekopp. This content is protected and may not be shared, uploaded, or distributed.
25

SOLUTION

© 2022 by Mark Redekopp. This content is protected and may not be shared, uploaded, or distributed.
26

Operator Overloading Review


Member or Non-member? Arguments
• How do you decide if you can make • For member function operator
the operator overload function a overloads, how many input
member function of the class? arguments are needed for
– If the left-hand side operand is a class operator+?
instance
– Only 1, the left side operand is 'this'
• When do you have to use a non-
member operator function? • for operator!
– If the left operand of an operator is – None, the left side operand is 'this'
NOT an instance of the class, you // arbitrary precision integer class
cannot use a member function class BigInt {
// arbitrary precision integer class ____ operator+(const BigInt& rhs);
class BigInt { ____ operator!();
... };
}; int main(){
int main(){ BigInt w, x, y, z;
BigInt x, y, z; w = x + y;
x = y + 5; bool flag = !w;
} }
© 2022 by Mark Redekopp. This content is protected and may not be shared, uploaded, or distributed.
27

Operator Overloading Review


Return types Chaining
• For class BigInt which • Do we need operator overload
models an arbitrary functions with 2-, 3-, 4-inputs,
precision integer, what etc. to handle various use
should the return type be cases?
for: – No, this is why the return type should
be BigInt to allow for chaining:
– Operator+: BigInt (by value)
x.operator+(y).operator+(z), etc.
– Operator==: bool
class BigInt { // arbitrary precision integer class
public: class BigInt {
BigInt operator+(const BigInt&); ...
bool operator==(const BigInt&); };
}; int main(){
int main(){ BigInt w, x, y, z;
BigInt w, x, y, z; w = x + y + z;
w = x + y; cout << w << " is a bigint!" << endl;
}
© 2022 by Mark Redekopp. This content is protected and may not be shared, uploaded, or distributed.
}

You might also like