|
|||||
CS201
Introduction to Programming
Lecture
Handout
Introduction
to Programming
Lecture
No. 37
Reading
Material
Deitel
& Deitel - C++ How to
Program
Chapter
11
11.3,
11.3.1, 11.4, 11.4.1
Summary
·
Overloading
Insertion and Extraction
Operators
·
Example
1
·
Example
2
·
Tips
Overloading
Insertion and Extraction
Operators
We are
already aware that while
overloading operators, functions are
written and spirit or
behavior
of the operators ( +, -, *, /
) is
maintained in their implementations. Similarly
the
operator's spirit is kept intact
while overloading stream insertion and
extraction
operators.
We get an
integer as input by writing
the following lines:
int
i;
cin
>> i;
Have a
look on the stream
extraction operator's ( >> )
behavior here. The
similar
behavior
is maintained when we overload this
stream extraction operator ( >> ) or
stream
insertion
operator ( << ).
There
are couple of important things to
take care of, before
starting implementation for
overloading an
operator:
The
first thing to see is the
type of the operator i.e. whether the
operator is binary or
unary.
The binary operator takes
two operands while unary
operator takes one.
The
number of
operands for an operator
cannot be changed while overloading
it.
Page
470
CS201
Introduction to Programming
Secondly,
the programmer has to take
care of, what an operator is
returning back. For
example, in
case of addition ( + ), it returns
back the result of addition. So
the cascading
statement
like a + b + c;
can be
executed successfully. In this case, at
first, b + c
is
executed.
The result of this operation
is returned by the operator
+, to
add it in the
variable
a. So in
actual, the operation is
carried out as:
a + ( b +
c).
We want
to overload stream extraction ( >> )
and insertion ( << ) operators
which are
actually
already overloaded. See the
code lines below:
1.
int
i = 123;
2.
double
d = 456.12;
3.
float
f = 789.1;
4.
5.
cout
<< i << " ";
6.
cout
<< d << " ";
7.
cout
<< f;
You
can see the lines 5, 6 and
7. The same stream insertion
operator ( << ) has been
used
with
different data types of int, double
and
float.
Alternatively, these lines (5, 6
and 7)
can be
written within statement of
one line:
cout
<< i << " "<< d << " "<<
f;
Similarly,
the stream extraction operator ( >>
) is used with different
data types in the
following
manner:
cin
>> i;
cin
>> d;
cin
>> f;
Here,
stream extraction operator is
used with different data
types of int, double
and
float.
The
three lines given above can
be written in one cascading
line:
cin
>> i >> d >> f;
The
file iostream.h
contains
the operator overloading declarations
for these stream
insertion ( << )
and extraction ( >> ) operators for
native data types. The
declarations
inside
this file look like
the following:
Page
471
CS201
Introduction to Programming
istream&
operator>>(char*);
istream&
operator>>(unsigned char* p) {
return operator>>((char*)p); }
istream&
operator>>(signed char*p) { return
operator>>((char*)p); }
istream&
operator>>(char& c);
istream&
operator>>(unsigned char& c) {return
operator>>((char&)c);}
istream&
operator>>(signed char& c) {return
operator>>((char&)c);}
istream&
operator>>(int&);
istream&
operator>>(long&);
#if
defined(__GNUC__)
__extension__
istream& operator>>(long long&);
__extension__
istream& operator>>(unsigned
long long&);
#endif
istream&
operator>>(short&);
istream&
operator>>(unsigned int&);
istream&
operator>>(unsigned long&);
istream&
operator>>(unsigned
short&);
#if
_G_HAVE_BOOL
istream&
operator>>(bool&);
#endif
istream&
operator>>(float&);
istream&
operator>>(double&);
istream&
operator>>(long double&);
istream&
operator>>( __manip func) {(*func)(*this);
return *this;}
istream&
operator>>(__imanip func) { return
(*func)(*this); }
istream&
operator>>(streambuf*);
In order
to use these insertion ( << ) and
extraction ( >> ) operators with
classes, we have
to
overload these
operators.
As
discussed in the previous
lectures, there are two
ways of overloading operators,
either
as class
members or non-members. But
these insertion ( << ) and extraction ( >>
)
operators
cannot be overloaded as members.
The reason is obvious as the
driving object
is on the
left side of the operator
for member operators. In case of
stream insertion ( << )
and
extraction operators ( >> ), the object on
the left side is either
cin
or
cout
usually.
These
cin
and
cout
objects
will remain intact for our
overloaded insertion and
extraction
operators.
Therefore, the overloaded
operators cannot be member operators.
Now, we are
left
with no option but to
overload these operators as
non-members. While overloading
these
operators as non-members, either we
can use setters
and
getters
of
the objects
(provided
that they are present as
part of the class interface) or
declare the operator as
the
friend
of
the class to access the
private
members
directly. Remember, we can
only
declare
friends of our
classes and not those of
library classes e.g., we
cannot declare a
function
as a friend of istream
or
ostream
class.
Normally, when define a class
(declare
functions as
friends inside
it), the friend
functions
are defined below the
class's
definition.
Page
472
CS201
Introduction to Programming
Here we
are going to declare our
overloaded operators as friends of our
classes. The
object on
the left of the operator
will be a stream object like
cin, cout
and on
the right will
be the
object of our class.
We should
be clear about the return
type of the overloaded operator as
the operator
function
has to support the cascading
operations. In case of stream insertion
operator ( <<
), the
operator function returns a
reference to the ostream
to
support cascading
operations.
An example prototype of stream insertion operator (
<< ) is as under:
ostream
& operator << ( ostream & output, Vehicle v
);
cout
object
will be replaced with its
reference output,
therefore, in the definition of
this
operator
function, output
will be
used as cout. Note
that the first parameter is
passed by
reference
and the compiler does
not allow it to pass by
value. The first object is
returned
back by
reference by the operator
function. That's why, the
compiler does not allow
to
pass
first parameter by value. We must be
remembering that the objects
passed by value
are
local to the function and
destroyed when the function
returns. Therefore, it does
not
make
sense to return references of
the objects, passed by value
to the function.
As we are
declaring this operator function as
friend
of
our class Vehicle, the
private
members
of Vehicle
will be
accessible to this operator
function. For example, tyre
is
a
private
data
member of type int
inside Vehicle
class
and inside the operator
function's
implementation,
we can access it by simply
writing v.tyre
as:
output <<
v.tyre;
The
above statement actually
is:
cout
<< v.tyre;
tyre
is of
native data type int. The
output
will
work for native data types
as it is actually
cout, which
is overloaded for all native
data type. We are constructing a
building using
the
basic building blocks. We can
use the already used
bricks to construct new
walls.
Similarly,
while writing out programs,
we implement our overloaded
operators using the
already
available functionality of native data
types.
Here is
how we overload stream insertion
operator ( << ) for our
Date
class:
#include
<iostream.h>
class
Date
{
friend
ostream& operator << ( ostream &
os, Date d );
// this
non-member function is a friend of
class date
...
...
};
ostream
& operator << ( ostream & os, Date d
)
{
Page
473
CS201
Introduction to Programming
os <<
d.day << "." << d.month << "."
<< d.year; // access private
data
// as
friend
return
os;
};
Likewise,
we can overload stream extraction
operator ( >> ). All the conditions
for
overloading
this operator are similar to
that of stream insertion operator (
>>). It cannot
be a member
operator, always a non-member
operator function, declared as
friend
of
the
class to
be overloaded for. It returns an object
of type istream
&, accepts
first parameter
of type
istream
&. There
is one additional restriction on extraction
operator ( >> ) i.e. the
second
parameter is also passed by
reference as that object is modified by
this operator
function.
For our Date
class, it
is declared as:
istream &
operator >> ( istream & input, Date & d
);
Note
that second parameter can
also be passed by reference
for insertion operator ( <<
)
but
that is not mandatory and
may be used to gain performance.
But in case of extraction
operator
( >> ), it is mandatory to have
second parameter of reference
type.
Example
1
Following
is our Date
class
containing the overloaded
insertion (
<< )
and extraction ( >> )
operators:
/* Date
class containing overloaded insertion and
extraction operators. */
# include
<iostream.h>
class
Date
{
public:
Date(
)
{
cout <<
"\n Parameterless constructor called
...";
month =
day = year = 0;
}
~Date (
)
{
//
cout
<< "\n Destructor called
...";
}
// Methods,
not directly related to the
example have been taken out
from the class
Page
474
CS201
Introduction to Programming
friend
ostream & operator << ( ostream
& os, Date d );
friend
istream & operator >> ( istream & is,
Date & d );
private:
int
month, day, year;
};
ostream
& operator << ( ostream &
os, Date d )
{
os <<
d.day << "." << d.month <<
"." << d.year; // access private
data of
//Date
being a friend
return
os;
};
istream &
operator >> ( istream & is, Date& d
)
{
cout
<< "\n\n Enter day of
the date: ";
cin
>> d.day;
cout
<< " Enter month of the
date: ";
cin
>> d.month;
cout
<< " Enter year of the
date: ";
cin
>> d.year;
return
is;
};
main(void)
{
Date
date1, date2;
cout
<< "\n\n Enter two
dates";
cin
>> date1 >> date2;
cout
<< "\n Entered date1
is: " << date1 << "\n
Entered date2 is: " <<
date2;
}
The
output of the program is:
Parameterless
constructor called ...
Parameterless
constructor called ...
Enter
two dates: ...
Enter
day of the date: 14
Enter
month of the date: 12
Enter
year of the date:
1970
Page
475
CS201
Introduction to Programming
Enter
day of the date: 05
Enter
month of the date: 09
Enter
year of the date:
2000
Entered
date1 is: 14.12.1970
Entered
date2 is: 5.9.2000
Example
2
Following
is an example of a Matrix
class,
where until now, we have not
overloaded
insertion ( << )
and extraction operators ( >>
).
/* Matrix
class, which is without overloading
stream operators */
#include
<iostream.h>
#include
<stdlib.h>
class
Matrix
{
private
:
int
numRows, numCols ;
float
elements [30] [30] ;
public
:
Matrix(
int rows , int cols )
;
void
getMatrix ( ) ;
void
displayMatrix ( ) ;
};
Matrix ::
Matrix ( int rows = 0 , int
cols = 0)
{
numCols =
cols ;
numRows =
rows ;
for (
int i = 0 ; i < numRows ; i ++
)
{
for (
int j = 0 ; j < numCols ; j ++
)
{
elements
[ i ] [ j ] = 0 ;
}
}
}
Page
476
CS201
Introduction to Programming
void
Matrix :: getMatrix ( )
{
for (
int i = 0 ; i < numRows ; i ++
)
{
for (
int j = 0 ; j < numCols ; j ++
)
{
cin
>> elements [ i ] [ j ] ;
}
}
}
void
Matrix :: displayMatrix ( )
{
for (
int i = 0 ; i < numRows ; i ++
)
{
cout
<< "| " ;
for (
int j = 0 ; j < numCols ; j ++
)
{
cout
<< elements [ i ] [ j ] << " " ;
}
cout
<< "|" << endl ;
}
}
void
main ( )
{
Matrix
matrix (2, 2) ;
matrix.getMatrix
( ) ;
matrix.displayMatrix
( ) ;
system (
"PAUSE" ) ;
}
The
operator functions ( <<, >> )
are not overloaded for
this program. A specific
function
getMatrix()
has
been called to get the
values for the matrix
object
this entirely a
different
way
than we used to do for
primitive data types. For
example, we used to get int
i
as; cin
>> i.
Similarly, we called a method displayMatrix()
to
display the values in the
matrix
object. We
can see here, if we overload
insertion ( << ) and extraction ( >> )
operators
then
the user of our class,
does not need to know
the specific names of the
functions to
input
and display our
objects.
Page
477
CS201
Introduction to Programming
The
changed program after overloading
insertion, extraction operators and
few additional
statements
to format the output properly:
/* Matrix
class, with overloaded
stream insertion and extraction
operators. */
#include
<iostream.h>
#include
<stdlib.h>
class
Matrix
{
float
elements[30][30];
int
numRows, numCols;
public:
Matrix (
int rows = 0 , int cols = 0
)
{
numRows =
rows;
numCols =
cols;
}
friend
ostream & operator << ( ostream
& , Matrix & );
friend
istream & operator >> ( istream & ,
Matrix & );
};
istream &
operator >> ( istream & input ,
Matrix & m )
{
for (
int i = 0; i < m.numRows; i ++
)
{
for (
int j = 0; j < m.numCols; j ++
)
{
input
>> m.elements [ i ] [ j ] ;
}
}
return
input;
}
ostream
& operator << ( ostream & output ,
Matrix & m )
{
for (
int r = 0; r < m.numRows; r++
)
{
for (
int c = 0; c < m.numCols; c++
)
{
output
<< m.elements [ r ] [ c ] << `\t'
;
}
output <<
endl;
}
return
output ;
Page
478
CS201
Introduction to Programming
}
int
main ( )
{
Matrix
matrix ( 3 ,3 );
cout
<< "\nEnter a 3 * 3 matrix
\n\n";
cin
>> matrix ;
cout
<< "\nEntered matrix is:
\n";
cout
<< matrix;
system (
"PAUSE" );
return
0;
}
The
output of the program is:
Enter a 3
* 3 matrix
45
65
34
23
72
135
90
78
45
Entered
matrix is:
45
65
34
23
72
135
90
78
45
Press
any key to continue . .
.
You
can see both the
operators are declared friends of the
Matrix
class so
that they can
directly
access the private members
of the Matrix.
The
insertion operator ( << ) is accepting
both the parameters left
and right by
reference.
We
already know that for
insertion operator ( << ), it is not
really required to pass
the
second
parameter (the Matrix
object in this
case) by reference but we
have used here to
gain
efficiency. The function is returning an
object ostream
&, i.e., it is
returning a
reference
to a ostream
object,
that actually is the
required in order to support
cascaded
operations
using this operator.
The
extraction operator ( >> ) is also
accepting both the
parameters by reference. But
for
this
operator, it is mandatory to accept
the Matrix
object by
reference because
this
Page
479
CS201
Introduction to Programming
function
is modifying that object. Similar to
the insertion operation, this
function is also
returning a
reference to istream
object in
order to support cascaded
operations.
Clearly
after overloading the operators <<
and >>, it is more
convenient for the
programmer to
use these already familiar
operators to display and input
the object data
members.
Readability of the program
has also comparatively
increased.
Tips
·
Stream
insertion ( << ) and extraction operators (
>> ) are always implemented
as
non-member
functions.
·
operator
<< returns a value of type ostream
& and operator >> returns a
value of type
istream & to
support cascaded
operations.
·
The
first parameter to operator << is
an ostream & object. cout is an example of
an
ostream
object. Similarly first parameter to
operator >> is an istream & object. cin
is
an example of an
istream object. These first parameters
are always passed by
reference.
The compiler won't allow you
to do otherwise.
·
For
operator >>, the
second parameter must also be
passed by reference.
·
The
second parameter to operator
<< is an object of
the class that we are
overloading
the
operator for. Similar is the
case for operator
>>.
Page
480
Table of Contents:
|
|||||