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

L02c OperatorOverloading

Copyright
© © All Rights Reserved
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
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