|
|||||
CS201
Introduction to Programming
Lecture
Handout
Introduction
to Programming
Lecture
No. 31
Reading
Material
Deitel
& Deitel - C++ How to
Program
Chapter
8
8.2,
8.3, 8.4, 8.6,
8.7
Summary
·
Lecture
Overview
·
What is
Operator Overloading and Why is it
Required
·
Where is
it Relevant to Apply
·
Operators
to Overload
·
Restrictions on
Operator Overloading
·
Examples
of Operator Overloading
·
Non-member
Operator Functions
·
Example
Program 1
·
Example
Program 2
·
Tips
Lecture
Overview
The
topic of this lecture is Operator
Overloading. In previous
lectures, we discussed
about it
a bit while discussing about
references. So we will see in detail
what is operator
overloading,
how to overload operators, where it is
relevant to apply and what
are the
restrictions on
it.
What is
Operator Overloading and Why
is it Required?
Operator
overloading is to allow the same
operator to be bound to more than
one
implementation,
depending on the types of
the operands.
Page
389
CS201
Introduction to Programming
As you
know that there are
standard arithmetic operators in
C/C++ for addition ( + ),
subtraction
( - ), multiplication ( * ) and division
( / ). We should only use
these operators
for their
specific purposes. If we want to
add two ints, say
i
and
j, the
addition will take
place in
the following manner i.e. i +
j. To
add two double
numbers,
we use the same
operator
and write d1 +
d2.
We may add two floats with
the help of the same
operator as
f1 +
f2.
Similarly other operations of
-, *
and
/
on
the primitive types (sometimes
called
as native or
built-in types) can be employed. In
other words, these operators
are already
overloaded
for primitive types in C++.
But these C++ operators
cannot be used for
classes
and their objects. We have to
write our own operator
functions that can work
with
objects.
Let's
take an example of complex numbers.
There are two parts of a
complex number i.e.
real
and
imaginary. As
complex numbers are part of
mathematical vocabulary, so the
mathematical
manipulations are done on them like
addition, subtraction and
multiplication.
Suppose, we write our own
class for complex numbers
named Complex,
but we
can't add two complex
numbers c1
and
c2
as
c1 + c2
because
until now we don't
know
how to write it. Although,
we are able to write a
function say cadd()
to
serve this
purpose.
Complex
cadd ( Complex c1, Complex
c2 ) ;
It
accepts two complex numbers
as parameters and returns
back the resultant
complex
number.
But the usage of this
function to add two complex
numbers is generally clumsy.
It gets
more cumbersome and complex
if we want to carry out
cascading operations
like
i + j +
k. It
is better to use the
standard operators of +, -, *
and
/
as
they are more
readable
and
elegant.
Where
is it Relevant to Apply?
Firstly,
the operator overloading gets
relevant whenever there is
the application of the
mathematical functions
of addition, subtraction, multiplication
and division. Complex
number is
one example of it. As discussed
earlier, in case of Date
class,
the operators can
be
effectively used to get the
future or past dates.
Secondly,
the operators are also
used sometimes in case of
non-mathematical
manipulation.
The example of String
class to
manipulate strings help us understand it
in
a better
way. The operator +
can be
used to concatenate two
strings. Previously, we
used
strcat()
function
declared inside string.h
header
file to concatenate two
strings. As
compared
to strcat(), the
use of +
to
concatenate two strings is
definitely easier and
more
readable. But there is a
little bit cost associated
with this process of
operators
overloading.
The
cost is involved whenever we
overload an operator. We have to
write a function and
make
use of the operator
semantics correctly while
implementing the function.
This
means
that the function written to
overload +
operator
should do addition or
concatenation
of strings in case of String
objects.
Page
390
CS201
Introduction to Programming
Operators
to Overload
There
are two types of operators
to overload:
1.
Unary
2.
Binary
Unary
operators are the ones
that require only one
operator to work. Unary
operators are
applied to
the left of the operand.
For example, ^, &, ~
and
!.
Binary
operators require two
operands on both sides of
the operator. +, -, *, /, %, =, <
and
>
are
examples of binary operators.
The
complete list of C++ operators
that can be overloaded is as
follows:
+
-
*
/
%
^
&
|
~
!
=
<
>
+=
-=
*=
/=
%=
^=
&=
|=
<<
>>
>>=
<<= ==
!=
<=
>=
&& |
|
++
--
-> *
,
[ ]
()
new
new[ ] delete delete[
]
The
following operators can't be
overloaded.
.
:
::
.*
?
sizeof
Let's
start with operator overloading
mechanism. Consider an object date
of
the Date
class.
The data member day
can be
accessed as follows:
date.day
=
2;
In this
statement, the day
data
member of the date
object is
accessed and assigned
value
2. This
expression (date.day) is driven by
the object name at
left.
Similarly,
while using operators, the
statement like a + b
is
driven by the object at the
left. In
this case, +
operator
function for the object a
will be
called and b
object is
passed
explicitly
to the +
operator
function as an argument. The
rules of function overloading
are
applied to
the operator overloading. We cannot
write two +
operator
functions with
exactly
identical parameters. Following
the overloading rules, the
two operator functions
have to
be different by the type or number of
arguments.
The
syntax of the prototype of the
overloaded operator function
is:
return-type
operator
operator-symbol
(parameter-list);
operator
is
the keyword here. An example of
this will be as
follows:
Complex
operator + (Complex & );
Page
391
CS201
Introduction to Programming
We
sometimes write only
operator to refer to the
operator function in our
discussion.
Restrictions
on Operator Overloading
There
are some restrictions on operator
overloading.
- The operator
overloading functions for overloading (), [], ->
or
the assignment (=)
Operators
must be declared as class
members.
- The arity
(number of operands) cannot be
changed. If you are overloading an
operator
that
requires two operands e.g.
*. It
cannot be used as a unary
operator that
requires
one
operand.
- No new
operators can be created.
Like in Fortran language, we
have **
as
`raise to
the
power (exponent) operator' but this
operator does not exist in
C++. Therefore, it
can't be
overloaded. Hence, only
existing operators of C++
are used.
- Overloading can't be
performed for the built-in (sometimes
called primitive or native)
data
types. For example, we cannot
change how two ints are
added. That means
that
operators
are overloaded to use with
defined data types like
classes.
- Precedence of an
operator cannot be changed.
For example, the *
has
higher
precedence
than +.
This
precedence cannot be
changed.
- Associativity of an
operator cannot be changed. If
some operator is right
associative,
it cannot
be changed to be left
associative.
Examples
of Operator Overloading
Let's
take the complex number's
class Complex
and
define a +
operator
function.
We know
that when we write the
following line:
x = y +
z;
y
and
z
operands
are take part in the
addition operation but there
is no change in them due
to this
operation. This is the +
operator's
functionality. The resultant is
being assigned to
the
variable x. This is
assignment operator's
functionality.
Now we
will discuss a little bit
about the assignment
operator as well. Let's say
we write
the
following statement for two
complex numbers c1
and
c2.
c1 = c2
;
Here c2
is
being assigned to c1. Will
this assignment work when we
have not written
any
assignment
operator function for
complex number? Apparently, it
looks that the
statement
will produce a compilation
error (as there is
assignment operator defined by
us)
but
this is not true. Whenever,
we write our own class
and compile it, the
compiler
automatically
generates a default assignment operator.
The default assignment
operator
makes a
member to member assignment. This works
fine unless there is a pointer
data
member inside
our class and that pointer
is pointing to some data inside
memory. For that
case
(when there is a pointer data member) we
have to write our own
assignment operator
Page
392
CS201
Introduction to Programming
otherwise
the default assignment operator
works fine for us.
That will be discussed in
the
subsequent
lectures.
By
definition of addition of complex
numbers, we know that
whenever two complex
numbers
are added, the real
part of one number is added
into the real part of
other
number.
Similarly, the imaginary
part of one number is added
to the imaginary part of
the
other
number. We also know that
when a complex number is
added to another
complex
number,
the resultant is also a
complex number consisting of real
and imaginary parts.
This
addition of real, imaginary parts
and return of resultant
complex number is the
functionality
of the +
operator
function we are going to
write.
Another
thing to decide for this +
operator is whether this operator will be
a member
operator
or a friend
operator.
Normally, operators are member
operators but there
are
situations
when they cannot be member
operators. In case of member operator,
following
is the
syntax of its prototype:
Complex
operator + (parameter-list);
For
member operator, the object on the
left side of the +
operator
is driving this +
operation.
Therefore, the driving object on
the left is available by this
pointer to
+
operator
function. But the object on
the right is passed
explicitly to the +
operator
as an
argument.
We can
define a member operator as under:
1.
Complex
Complex :: operator + (Complex
c)
2.
{
3.
Complex
temp ;
4.
temp.real
= real + c.real ;
5.
temp.imag =
imag + c.imag ;
6.
return
temp ;
7.
}
Let's
see this code line by
line.
Line 1
indicates that the return
type is Complex, it is an
operator
+ function
and it is
accepting
a Complex
object by
value as an argument.
In line
3, a local Complex
object is
declared, called temp.
In line
4, real
part of
the calling object (that is
the one, driving) on the
left of the +
operator
is being added to the real
part of
the object c, where
c
is
passed as an argument.
In line
5, imag
part of
the calling object (that is
the one, driving) on the
left of the +
operator
is being added to the imag
part of
the object c, where
c
is
passed as an argument.
In line
6, the Complex
object temp
containing
the resultant of +
operation
is being
returned
by value.
In our
code, we can write something
as:
Complex
c1, c2, c3 ;
...
...
c3 = c1 + c2
;
Page
393
CS201
Introduction to Programming
In the
above statement ( c3 = c1 + c2; ), c1 is
the object that is calling or
driving the
+
operator.
c2
object is
being passed as an argument to
the +
operator.
So c1 and c2
objects
are added by the +
operator
and resultant Complex
object containing
the addition
of these
two numbers is returned
back. That returned Complex
object is
assigned to the
c3
Complex object
using the default assignment
operator (that is created by
the C++
compiler
automatically).
What
happens if we want to add a
double
number to
a complex number (a instance
of
Complex)? Like
the following:
c3 = c1 + d
;
This
+
operation
is driven by the c1
object of
Complex
while
double number d
of
type
double
is
passed as argument. Therefore,
our above written +
operator
is not useable for
this
operation of addition. We need to
overload +
operator
for accepting a parameter
of
type
double, i.e. we
need to write another
operator function. The
definition of this
newly
overloaded
+
operator
is:
Complex
Complex :: operator + (double
d)
{
Complex
temp ;
temp.real
= real + d ;
// d is
added into the real
part
temp.imag =
imag ;
return
temp ;
}
By now,
you should have noticed that
operator overloading and function
overloading are
quite
similar.
When we
write the following
statement:
c3 = d +
c1;
The
operand on the left of +
operator
is a double
number
d.
Therefore,
this + operation
should be
driven by (called by) the double
number.
Until now, we have not
written such
an
operator. Our previously written
two +
operators
were driven by the Complex
object.
Operator
functions, not driven by the class type
objects, are kept as friends to the
class.
friend
is
the keyword used to declare
such functions. A friend
function
to a class also
has
access to the private members of
that class.
friend
Complex operator + (double d,
Complex c)
{
Complex
temp;
temp.real
= d + c.real; // d is added into
the real part of c
temp.imag =
c.imag;
return
temp;
}
Page
394
CS201
Introduction to Programming
You
might have noticed that all
the three overloaded +
operator
functions are accepting
and
returning variables by value. To make
these functions better, we can
also use
references.
So our first member +
operator's
prototype can be rewritten
as:
Complex&
operator + (Complex&
c);
Now
this operator function is
accepting a complex number
Complex
by
reference and
returning a
reference to the resultant
complex number.
As
discussed above, in case of
assignment, the default assignment
operator is used
because
we have not implemented (overloaded)
our own assignment operator
(`=').
But in
case, we want to perform the
following operation where the
two operands are
added
and the resultant is
assigned to one of them
as:
c1 = c1 +
c2;
There is
one operator (+=) that
can be used to do both the
operations of addition and
assignment
instead of doing these operations
separately within operator
+ and
operator
=.
So we
can overload this one
operator (+=) here to
make the code more efficient
and
reduce
our work. Therefore, instead
of writing:
c1 = c1 +
c2;
We will
write:
c1 +=
c2;
We will
write our operator
+= as:
void
Complex :: operator += ( Complex& c
)
{
real +=
c.real;
imag +=
c.imag;
}
Non-member
Operator Functions
Now we
are much clear that
when an operator function is implemented
as a member
function,
the leftmost operator must be a
class object or reference to a class
object of the
operator's
class.
When an
operator function is implemented as a
non-member function, the
left-most
operand
may be an object of the operator's
class, an object of a different class, or
a built-
in type.
Now we discuss it in a detailed
manner.
We can
always write our operators
as non-member functions. As a non-member
functions,
the binary operators like
+
gets
both the operands as
arguments. One thing
to
take
care of while writing non-member
functions that they cannot
access the private
Page
395
CS201
Introduction to Programming
members
of classes. Actually, this is
just to this reason that we
make those non-member
functions as
friends to the
classes whose private
data
members are required to
be
accessed.
But the question arises,
can we write a non-member operator
function without
making it
a friend
of a
class. The answer to this
question is yes; If there
are public
member functions to
access the private data
members of the class then
they serve the
purpose.
In this case of Complex
class,
let's say we have two
public member functions:
double
real( );
double
imaginary( );
to access
the private
data
members real
and
imag
respectively.
Then we can write
non-
member operator
+ function
as:
Complex
operator + (Complex& c1, Complex&
c2)
{
Complex
temp;
temp.real
( c1.real() + c2.real() );
temp.imaginary
( c1.imaginary() + c2.imaginary()
);
return
temp;
}
But this
non-member operation functions without
declaring a friend
of
the class is
definitely
slower than the member
function or a friend
one.
The reason for this is
obvious
from
the code that it is making
three additional function calls of real()
and
imaginary()
for
each private data member. Also it is
not easy to write as
compared to member
functions.
Therefore, it is recommended to write
the member functions for
operators
instead
of non-members.
Let's
take an example where the operators
are performing a non-arithmetical
operation.
We are
writing a class String
for
strings manipulation
as:
class
String
{
private
:
char
string [ 30 ] ;
public
:
String (
)
{
strcpy ( string , "" )
;
}
void
getString ( )
{
cout
<< "Enter the String : "
;
cin
>> string ;
Page
396
CS201
Introduction to Programming
}
void
displayString ( )
{
cout
<< "The String Is : " << string <<
endl ;
}
// Declaration
(prototype) of overloaded sum
operator
String
operator + ( String & s ) ;
};
We want
to write + operator to concatenate
two strings. Firstly, we
will see the
operator's
behavior
in ordinary context (behavior with primitive
variables for example) and
try to
implement
the same behavior for
this class. We want to
concatenate two strings
(two
String
objects)
and then assign the
resultant string to a new String
object. Here is
how
we will
write +
operator
function.
String
String :: operator + ( String &s
)
{
String
temp;
//
Declared object temp of String
type
strcpy ( temp.string ,
"" );
//
Initialized the temp with
empty string
strcat (
temp.string , string );
//
Concatenated the driving
object's string to
// temp
object
strcat (
temp.string , s.string );
//
Concatenated the argument's string to
the
// temp
object
return
temp;
//
Returned the temp object
}
As you
might have guessed already,
the String
object on
the left will be the
one to drive
this
+
operation
and the second String object
on the left of +
will be
passed as an
argument
to this function. Note that
we are not doing the error
checking here, the size of
the
resultant string temp
may
increase the array size 30
(
the array size defined in
the
class).
Example
Program 1
Rudimentary
implementation of a class named
Complex
class to
cater complex
numbers.
A +
operator function has been
implemented to add two complex
numbers.
/* This
program implements the basic
class for complex numbers
and demonstrates +
operator
function */
#include
<iostream.h>
class
Complex
Page
397
CS201
Introduction to Programming
{
private
:
double
real ; // Real
Part
double
imag ; // Imaginary
Part
public
:
/*
Parameterless Constructor */
Complex (
)
{
cout
<< "\n Parameterless Constructor called
..." ;
}
/*
Parameterized Constructor */
Complex (
double r, double i )
{
cout
<< "\n Parameterized Constructor
called ...";
real = r
;
imag = i
;
}
/* Setter
of real data member */
void
real ( double r)
{
real = r
;
}
/* Getter
of the real data member
*/
double
real ( )
{
return
real ;
}
/* Setter
of the imag data member
*/
void
imaginary ( double i )
{
imag = i
;
}
/* Getter
of the imag data member
*/
double
imaginary ( )
{
return
imag ;
}
Page
398
CS201
Introduction to Programming
/* A Function to
display parts of a Complex object
*/
void
display ( )
{
cout
<< "\n\n Displaying parts of
complex number ...";
cout
<< "\n Real Part : " <<
real << endl ;
cout
<< " Imaginary Part : " <<
imag << endl ;
}
/* Declaration
(prototype) of overloaded sum operator
*/
Complex
operator + ( Complex & c2 ) ;
};
Complex
Complex :: operator + ( Complex & c1
)
{
cout
<< "\n Operator + called
...";
Complex
temp ;
temp.real
= real + c1.real ;
temp.imag =
imag + c1.imag ;
return
temp ;
}
void
main ( )
{
Complex
c1 ( 1 , 2 ) ; // Consturct an object using
the parameterized
constructor
Complex
c2 ( 2 , 3 ) ; // Consturct another object
using the
parameterized
//
constructor
Complex
result ; // Construct an object using a
parameterless constructor
result =
c1 + c2 ; // Call the Operator + to
add two complex numbers
(c1 & c2)
// and
then assign the result to
'result' object
result.display ( ) ;
// Display the result object
contents
}
The
output of the program is as
follows:
Parameterized
Constructor called ...
Parameterized
Constructor called ...
Parameterless
Constructor called ...
Operator
+ called ...
Parameterless
Constructor called ...
Displaying
parts of complex number
...
Real
Part : 3
Page
399
CS201
Introduction to Programming
Imaginary
Part : 5
The
+
operator
function can be enhanced to
return reference of Complex object. We
can
also
implement +=
operator.
+=
operator
and the enhanced operator
+
are
implemented
as:
Complex
& Complex :: operator + ( Complex
& c1 )
{
real =
real + c1.real ;
imag =
imag + c1.imag ;
return
*this;
}
// Declaration
(prototype) of overloaded sum assignment
operator definition
Complex
& Complex :: operator += ( Complex
& c2 )
{
real +=
c2.real ;
imag +=
c2.imag ;
return
*this;
}
Example
Program 2
Rudimentary
Implementation of String class to
manipulate strings. It uses +
operator
to
concatenate
strings.
/* This
program implements the basic
class for strings and
demonstrates + operator
function
to concatenate two
strings*/
#include
<iostream.h>
#include
<string.h>
class
String
{
private
:
char
string [ 30 ] ; // Array to store
string
public
:
/*
Parameterless Constructor */
String (
)
{
strcpy ( string , "" )
;
Page
400
CS201
Introduction to Programming
}
/* Getter
function of string */
void
getString ( )
{
cout
<< "Enter the String: "
;
cin
>> string ;
}
/* Function to
display string */
void
displayString ( )
{
cout
<< "The String is : " << string <<
endl ;
}
// Declaration
(prototype) of overloaded sum
operator
String
operator + ( String & s ) ;
};
String
String :: operator + ( String &s
)
{
String
temp ;
strcpy ( temp.string ,
"" ) ;
strcat (
temp.string , string );
strcat (
temp.string , s.string );
return
temp;
}
void
main ( )
{
String
string1 , string2 ;
//
Declared two String
objects
string1.getString ( )
;
// Get
string for string1 object
string2.getString ( )
;
// Get
string for string2 object
String
hold = string1 + string2 ;
//
Concatenate string1 and string2 and
store the
// result
in hold object
hold.displayString
( ) ;
//
Display the string
}
The
output of the above program is as
follows:
Enter
the String: Operator
Enter
the String: Overloading
The
String is : OperatorOverloading
Page
401
CS201
Introduction to Programming
Tips
Operator
Overloading is quite similar to Function
Overloading.
-
There
are two types of operators
to overload: unary and binary.
-
C++
built-in operators work for
built-in (primitve) types
but for user defined
data
-
types,
user has to write his/her
own operators.
There
are some restriction while
performing Operator Overloading. For
example,
-
only
existing C++ operators are
overloaded without creating a
new one in the
language.
Also, it should not impact
the type, semantics (behavior),
arity (number of
operands
required), precedence and
associativity of the
operator.
For
binary member operators, operands on
the left drives (calls) the
operation.
-
Operator
functions written as non-members but
friends of the class, get
both the
-
operands
as their arguments.
Operators
can be written as non-members
and even without making them
friends. But
-
this is
tedious and less efficient
way, therefore, it is not
recommended.
Page
402
Table of Contents:
|
|||||