Title: Operator overloading
1- Operator overloading
- We would like to assign an element to a vector or
retrieve an element stored in a vector using the
indexing operator rather than the at() member
function - IntVector myVector( 4 )
- myVector2 34
- cout ltlt myVector2 ltlt endl
- We will see how to add this to the IntVector
class - we need to overload the indexing operator
- we will first examine operator overloading for
the simpler Complex class - then we will return to the IntVector class.
2The complex number class Complex numbers have the
form a ib where a and b are real numbers and
i is such that i2 -1 Suppose that z1aib and
that z2cid then z1 z2 (a c) i (b
d) z1 z2 (a c) i (b d) z1 z2 (ac
bd ) i (ad bc) z1 / z2 ( (ac bd) i (bc
ad) ) / (c2 d2) Let us now consider a
declaration of the Complex class
3class Complexpublic // not yet complete
Complex( void ) //Post complex number is 0
0i Complex( double real, double imag )
//Post complex number is real imagi
void setComplex( double real, double imag )
//Post complex number is real imagi
double getReal( void ) const //Post real
part has been returned double getImag( void )
const //Post imaginary part has been
returned private double realPart
double imagPart
4- Implementing complex arithmetic in C
- the basic member functions are easy
- a complete version is on the course website
- we will focus on adding overloaded operators
- A client can perform operations such as
- Complex num( 5, 4 )
- cout ltlt Real part ltlt num.getReal() ltlt
Imaginary part ltlt num.getImag() ltlt endl - But code such as the following will not compile
- Complex num1( 5, 4), num2( 6, 4), num3
- num3 num1 num2
- Reason the operator has not been defined
when the left and right operands are of type
Complex
5- Overloading operators in C
- We can define how to add two objects of type
Complex - Any binary operator can be
- overloaded as a member function of a C class
- the left operand must be an instance of that
class - Lets recall our client code
- num3 num1 num2
- the left operand is of type Complex
- so we can define an overloaded operator
- the alternate syntax for invoking operators is
- num3 num1.operator( num2 )
6Overloaded addition operator Declaration of
operator (header file / class declaration)
Complex operator( const Complex rComplex )
const Implementation (source file / class
definition) Complex Complexoperator( const
Complex rComplex ) const Complex
sum sum.realPart realPart
rComplex.realPart sum.imagPart imagPart
rComplex.imagPart return sum
7Overloaded subtraction operator (no temp
variable) Declaration of - operator (header file
/ class declaration) Complex operator-(
const Complex rComplex ) const Implementation
(source file / class definition) Complex
Complexoperator-( const Complex
rComplex ) const return Complex( realPart -
rComplex.realPart, imagPart -
rComplex.imagPart ) We use the parameterized
constructor to avoid a local variable
8- Other overloaded operators in Complex
- The binary arithmetic operators can all be
overloaded - subtraction operator
- multiplication operator
- division operator/
- What about the assignment operator?
- no need to define operator
- the compiler-provided default works just fine
- component-wise assignment is what we want for
Complex - Are there others operators we need to overload?
9Overloading the strictly less than operator One
complex number is less than another iff both
parts are less than the others parts Declaration
of lt operator (header file / class declaration)
bool operatorlt( const Complex rComplex )
const Implementation (source file / class
definition) bool Complexoperatorlt( const
Complex rComplex ) const return
( realPart lt rComplex.realPart ) (
imagPart lt rComplex.imagPart ) Not quite.
One part has to be strictly less than, the other
can be equal.
10Overloading other comparison operators Declaration
of lt operator does not tell the compiler about
gt lt gt ! Each of these
must be declared (and defined) separately! bool
Complexoperatorgt( const Complex
rComplex ) const return ( realPart gt
rComplex.realPart ) ( imagPart gt
rComplex.imagPart ) You might think the
compiler would synthesize a component-by-component
operator, but it will not.
11- Input and output operators
- Consider the following client code
- Complex num1, num2, num3cout ltlt Please enter
two complex numbers (aib)cin gtgt num1 gtgt
num2num3 num1 num2cout ltlt First num
Second num ltlt num3 ltlt endl - the compiler now has no problem adding num1 and
num2 - but it does not know how to extract a complex
number from an input stream - and it doesnt know how to insert one into an
output stream - We must provide overloaded ltlt and gtgt
operators
12- Stream (I/O) variables
- the variable cin is an instance of a class named
istream - the variable cout is an instance of a class
named ostream - they are declared in the library header
ltiostreamgt - Consider the following client code
- Complex num( 3, 4 )
- cout ltlt num ltlt endl
- the ltlt operator is left associative
- it returns a reference to its left operand when
it is evaluated (just like ) - the expression above is evaluated as
- ( ( cout ltlt num ) ltlt endl )
13- Overloading the ltlt operator for the Complex class
- the left operand is an instance of the ostream
class - it is not an instance of the Complex class.
- so the ltlt operator cannot be overloaded in
Complex - We therefore must declare the ltlt operator as a
friend - friend ostream operatorltlt( ostream
outStream, const Complex number ) - the friend declaration appears within the class
declaration - a friend of a class is not a member function of
the class - however, it can access the private members of
the class - it cannot change the private members because of
the const
14Implementing the ltlt operator for the Complex
class ostream operatorltlt( ostream outStream
, const Complex number ) //
Non-member function // Uses cout and ltlt from
iostream library // Uses fabs() from cmath
library // Pre outStream is a valid ostream //
Post number is output to the given ostream in //
the form realPart /- imagParti
outStream ltlt number.realPart ltlt (
number.imagPart lt 0 ? "-" "" )
ltlt fabs( number.imagPart ) ltlt "i"
return outStream C
math absolute value function
15Operator precedence In C operators are applied
in order of their precedence outStream ltlt
number.getReal() ltlt (
number.getImag() lt 0 ? "-" "" )
ltlt fabs( number.getImag() ) ltlt
"i" return outStream Parentheses are
required for this expression because of
precedence in this example C conditional
operator boolean ?
expression expression
16- Overloading the gtgt operator for the Complex class
- the left operand is an instance of the istream
class - it is not an instance of the Complex class.
- so we (again) declare the gtgt operator as a
friend - friend istream operatorgtgt( istream
inStream, const Complex number ) - Consider the following client code
- Complex num1, num2
- cin gtgt num1 gtgt num2
- the gtgt operator is (again) left associative
- and it returns a reference to its left operand
- ( ( cin gtgt num1 ) gtgt num2 )
17Implementing the gtgt operator for the Complex
class istream operatorgtgt( istream inStream ,
Complex number ) //
Non-member friend function // Uses cin and gtgt
from iostream library // Pre inStream is a valid
istream input is valid // Post number has been
read from the keyboard with // format
realPart/-imagParti with no spaces
char iChar // used to read 'i, which is
ignored inStream gtgt number.realPart
gtgt number.imagPart gtgt iChar
return inStream
18- A caution about declaring friend methods
- Declaring a function to be a friend violates the
principle of encapsulation - a function external to the class can access
private data - a function external to the class can change
private data - a function external to the class can invoke
private methods - normally we do not want to allow this
- sometimes we have to do this, as for ltlt and gtgt
- only do this when it is absolutely necessary
- we can declare a non-friend function calling
public methods - istream operatorgtgt( istream inStream,
- Complex number )
19Non-friend ltlt operator for the Complex class
ostream operatorltlt( ostream outStream ,
const Complex number ) //
Non-member non-friend function // Uses cout and
ltlt from iostream library // Uses fabs() from
cmath library // Pre outStream is a valid
ostream // Post number is output to the given
ostream in // the form realPart /-
imagParti outStream ltlt number.getReal()
ltlt ( number.getImag() lt 0 ? "-" ""
) ltlt fabs( number.getImag() )
ltlt "i" return outStream Less
efficient because of method calls, but not too
ineffcient
20Non-friend gtgt operator for the Complex class
istream operatorgtgt( istream inStream ,
Complex number ) // Non-member
friend function // Uses cin and gtgt from iostream
library // Pre inStream is a valid istream
input is valid // Post number has been read from
the keyboard with // format
realPart/-imagParti with no spaces
char iChar // used to read 'i'
double real, imag // read the
two parts inStream gtgt real gtgt imag gtgt
iChar Complex value( real, imag ) // build
result number value // set
value return inStream Less efficient
because of method calls, but not too ineffcient
21- Overloading other operators (mixing types)
- Now consider the following client code that will
not compile - Complex num1( 4.5, 5.0), num2double value
5.5 - num2 num1 value
- the overloaded operator only works when both
operands are of type Complex - but here the right operand is of type double.
- We can now proceed in two ways
- supply another overloaded operator that takes
a left operand of type Complex and a right
operand of type double - supply a convert constructor that converts a
double to an object of type Complex
22Option 1 Supply another overloaded operator
Complex Complexoperator( const double
right ) const // Member operator function //
Post the sum of this Complex and the double
right is returned Complex sum
sum.realPart realPart right sum.imagPart
imagPart return sum
23- Option 2 Supply a convert constructor
- Declaration in the header file
- Complex( double num )//Post complex number is
num 0i - Implementation in the source file
- ComplexComplex( double num )
- //Post complex number is num 0i
- realPart num
- imagPart 0.0
-
- can be called explicitly in client code
- can also be called implicitly by the compiler as
needed
24Explicit conversion The programmer specifies use
of a conversion constructor Complex num1( 4.5,
5.0), num2double value 5.5 num2 num1
Complex( value ) This is an example of type
casting (or type conversion) double num
6.785int rounded int( num 0.5 ) or char
alpha Acout ltlt int( alpha ) ltlt endl
25Implicit conversion The compiler decides to use
a conversion constructor void myFunc( Complex
param ) double value 5.5 myFunc( value
) The convert constructor is called implicitly
to convert value to a Complex when it is passed
as a parameter to myFunc
26- Which is best Option 1 or Option 2?
- Advantages for Option 1
- no implicit conversion
- the more explicit our code is, the easier it is
to debug - Disadvantage for Option 1
- we may have to overload other versions of the
operator - Complex operator( double num, const Complex
rComplex)//Post the sum of (num 0i) and
rComplex is//returned - double num 6.5Complex complex1( 5.5, 3.5 ),
complex2 - complex2 num complex1
- Left operand is double and right operand is
Complex
27- Advantages for Option 2
- a single constructor that converts a double to
Complex - one version of the overloaded operator
- we can perform addition on many combinations of
operands - double num 6.5Complex complex1( 5.5, 3.5 ),
complex2( 0.5, 1.5 ), complex3 - complex3 complex1 complex2complex3
Complex( num ) complex2complex3 complex1
Complex( num ) - Disadvantage
- objects can be implicitly converted by the
compiler - this can sometimes lead to code that is
difficult to debug
28- The explicit keyword
- The disadvantage of Option 2 can be removed by
using the explicit keyword - add this keyword to the declaration of the
constructor - this prevents the compiler from implicitly
converting - so a double will not become a Complex
- only explicit invocations of the convert
constructor are allowed - public explicit Complex( double value )
-
29Operator overloading the indexing operator We
now (finally) overload the indexing operator
for our IntVector class. For the same reason
that we have two versions of the at() member
function, we need to declare two versions of the
overloaded indexing operator Accessor int
operator( int index ) const// Post if 0 lt
index lt size() the element at// index is
returned, otherwise program aborted Mutator int
operator( int index )// Post if 0 lt index lt
size() the element at// index is returned,
otherwise program aborted
30- Implementing the overloading indexing operator
- The implementation is exactly the same as the
corresponding at() functions - simply call the at() function in the operator
function - int IntVectoroperator( int index ) const
return at( index ) - this is concise
- but it is a little inefficient because of the
function call - this operator may be called many times by a
client - duplicating the code in at() is more efficient
- but code is bloated if we have blocks of
duplicate code
31- The inline keyword
- We can actually have the best of both approaches
- the concise version of the operator
- no overhead for a function call to at(
- inline int at( int index ) const return
value index -
- The inline keyword suggests that
- the compiler should replace each call to the
inline function with the body of that function - the compiler should avoid the function call
- the compiler can ignore the inline keyword if it
sees fit!