|
|
Summary
18) Recap
19) Overloading Minus Operator
20) Operators with Date Class
21) Unary Operators
Recap
Before further discussing the concept of the ‘Overloading’, we will recapture the
things
dilated upon in the previous lecture. It is necessary to know that new operators
i.e. new
symbols cannot be introduced. Only existing symbols can be overloaded. Overloading
of
operators is exactly like writing functions. However, one should remain close to
the
original meaning of the operator. Similarly, it is good not to define something
in opposite
terms e.g. ‘plus operator is doing subtraction or multiplication operator carrying
out
division’. We can do that but it will ultimately a bad thing for a programmer. It
makes
our program practically unreadable and mis-interpretable. Under operator overloading
technique, the binary and unary operators will remain unchanged that is we cannot
make
unary operator work as binary operator or vice versa. In the previous lectures,
we also
came across some concepts in terms of driving force behind the operator, e.g. in
case of
binary operator, the driving force is left hand operand. We have also studied when
to use
member operators and non-member operators. Today we continue discussion on ‘use
of
operators’.
Overloading Minus Operator
Let’s define minus operator ( - ) with special reference to the complex class. The
process
of defining the minus operator is quite similar to that of the plus operator. Let’s
first
understand the action of minus operator. It is a binary operator, having two arguments.
In
this case, both the arguments will be complex numbers. When we subtract two complex
Page 404
numbers, it always return a complex number. Here the subtraction of complex numbers
is
defined as, ‘subtract the real part from real part and subtract the imaginary part
from the
imaginary one”. So a member operator will look like as under:
Complex operator – (Complex c)
As we are defining it as a member operator, only one argument will be passed to
it. It is
going to be on the right hand side of the minus operator. The left-hand-side will
call this
as it is already available to this function. In the body, we will declare a temporary
Complex number. This means that the real part of this temporary complex number is
the
difference of the calling Complex number and the Complex number passed as argument
i.e.:
tmp.real = real – c.real;
In the next line, we calculate the difference of imaginary part as:
tmp.imag = imag – c.image;
and return the tmp Complex number. By defining, the minus operator does not mean
that
minus equal operator has also been defined. If we want to overload the minus equal
operator (-=), it is necessary to define it. Let’s see how the defining process
is carried out.
Minus equal to operator like the plus equal to operator behaves in the way that
the value
of calling party (i.e. the complex number which is on the left hand side) will also
be
changed. So now we will see that the number itself changing the value when it takes
part
in the minus equal to operator. Again, we will make this a member function. So only
one
argument will be passed to it. The complex number will be on the right hand side
of the
minus equal to operator. In the body of the function, there is no need of any temporary
complex number as we are going to change the number on the left hand side of the
minus
equal to operator. We can write it as:
real -= c.real;
imag -= c.image;
Here c is the complex
number which is passed as an argument. Now the minus equal to (-
=) operator, used in the above statements, is an ordinary minus equal to operator
for the
integers defined by the C++. So this is a classic example of overloading i.e. the
operator
being overloaded is using the original or basic operator of same type. That is the
end of
this function. The original number has been changed. We can return its reference.
It
depends on its usage.
Here is the code:
// The minus operator definition
Complex Complex::operator - ( Complex c )
{
Complex tmp; // defining a temporary var
Page 405
tmp.real = real - c.real;
tmp.imag = imag - c.imag;
return tmp;
}
// The -= operator definition
Complex Complex::operator -= ( Complex c )
{
real -= c.real ;
imag -= c.imag ;
}
Last time, we discussed the
string class besides defining the plus operator as joining the
two strings. Can we define minus for the
string class? Is the minus
operator relevant to
the class string?
For me it does not. Unless we come with some very artificial definition.
Suppose we have a string as “This is a test” and a second string as “test”. The
subtraction
of these two strings means the deletion of a word or words of second string from
the first
string. It may make some sense in this example. What will happen if the second string
contains “My name is xyz”. The subtraction of these strings does not make any sense.
The thing we need to understand at this point is that every operator does not make
sense
for every class. Operators should be used only when these make some common sense
so
that reader can understand it easily. When you add two strings, it makes lot of
sense. We
can use either cat function or write this plus operator. As subtraction of strings
does not
make much sense, so it is not advisable to define it. Only define things that are
selfexplanatory,
readable and understandable.
Operators with Date Class
We have so far been using the
Date class. Let’s think what operators make sense for
Date
class. What will be the meaning of plus operator or minus operator? Here we want
to
remind you a key thing i.e. “Paying attention to detail”. Suppose you have some
date and
want to add some number to it like today’s date plus 5. Does that make sense to
you? We
will get a new date by adding five to today’s date i.e. date after five days. Similarly,
if we
want to subtract, say 10 from today’s date, we should get the date of ten days before.
Here is the usage of plus and minus which makes some sense. Can we subtract two
dates
together like subtraction of 1st Jan. 2002 from 15th
Oct. 2002. What meaning it will
convey? Perhaps nothing.
Let’s consider the addition of a number to a date. Adding an integer to some date,
according to the definition we will get some date in the future. The
Date object will be
returned from this function. We need a new date after the addition of integer number.
We
are defining this as a member-function so that the
Date object that is calling
it, will be
passed to the function. The integer that is on the right hand side should be passed
as an
argument. Therefore in the parenthesis, we will have the integer. Now let’s discuss
it in
detail. How can we add an integer to some date? Let’s take today’s date. Write it
in your
copy and see how can five be added to it. If you try to add a number to date, there
are so
Page 406
many possibilities that can happen. Suppose, today is second day of the current
month.
After adding five to it, we will get 7th of this month. That was case
I. Let’s take the case
II. Today is 27th of any month. Now what will be the new date after
adding five. First
thing, which is very obvious, that the month will get changed. But what will be
the date
of this new month. It depends whether there are 30 days or 31 days in this month.
It may
be the month of February. Is it the leap year or not? If it is non-leap year, there
will be 28
days in February. Otherwise there will be 29 days. What is a leap year? There are
rules to
determine whether the year is leap year or not. If the year is divisible by four,
it will be
leap year. Similarly, being a century year, it may be divided by 400. Then again
it is a
leap year. Now we have seen that there are many cases when we are adding five to
27th of
any month. Two things happen. The month is changed and the date changes according
to
the days in the month. What if it is the 27th of the December? Now
you want to add five
days. There are 31 days in December, after adding five it will be 1st
of next month. We
may have declared an array of twelve months. As December is the twelfth month, the
last
month of the year, so we have to go to first month of the next year. Here the year
has also
changed. We will also need to increment 1 to year too. It seems very simple that
we have
to add an integer number of days to some date. It becomes a complex function. Now
suppose we have written this complex function and embedded all the rules in it.
Then our
life will become much easier. Suppose our semester starts from any date. After adding
the
period of semester, we will get the end date of the semester. We can do date arithmetic.
This is a classic example of “paying attention to detail”. To use the class for
general
purposes, we cannot miss even a single case. If you want to publish this class for
others
to use, you need to pay attention to detail and make sure that your class handles
all of the
stuff.
Here is the complete code of the program.
File “Date.h”
// The Date class is defined here
class Date{
private:
int day;
int month;
int year;
int daysOfMonth(Date d); // returns the no of days in a month
static const int daysInMonth[]; // array containing the 12 month’s days
bool leapYear(int); // tells the year is leap year or not
public:
Date(int d = 1, int m = 1, int y = 1900); // constructor with default arguments
void setDate(int, int, int); // set the date with given
arguments
void display(); // Display the date on the screen
// operators prototypes
Date operator ++ (); // pre increment operator used as ++date1
Date operator + (int); // Plus operator used as date1 + 5
Page 407
};
// The implementation of the date class.
// initializing the no of days, take 0 for month zero.
const int Date::daysInMonth[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30,
31};
// Displaying the function on the screen
void Date::display()
{
cout <<"\nDate:" << day << "-" << month << "-" << year;
}
//constructor of the date
Date::Date(int d, int m, int y)
{
setDate(d, m, y);
}
// setting the date as given arguments
void Date::setDate(int d, int m, int y)
{
year = y;
// if month is wrong then set it to 1
if (month < 1 && month > 12)
month = 1;
else
month = m;
// if day is wrong then set it to 1
if (month == 2 && leapYear(y))
if (d >=1 && d <=29)
day = d;
else
day = 1;
else
if( d >= 1 && d <= daysInMonth[month])
day = d;
else
day = 1;
}
// This function return the number of days in a month
int Date::daysOfMonth(Date d)
{
if (d.month == 2 && leapYear(d.year)) // if leap year then Feb is 29
return 29;
else
Page 408
return daysInMonth[d.month];
}
// Testing that the year is leap or not.
bool Date::leapYear(int y)
{
if ( (y%400 == 0) || (y%100 != 0 && y%4 == 0))
return true;
else
return false;
}
// + operator overloaded for the date. Used as date1 + 5
Date Date::operator + (int numberOfDays)
{
for (int i = 1; i <= numberOfDays; i++)
{
++(*this); // calling the pre increment operator
}
return *this;
}
// Pre increment operator
Date Date::operator ++ ()
{
if (day == daysOfMonth(*this) && month == 12) // end year
{
day = 1;
month = 1;
++year;
}
else if(day == daysOfMonth(*this)) // end month
{
day = 1;
++month;
}
else // not the last day of the month
{
day++;
}
}
The main program is:
#include <iostream.h>
#include "date.h"
Page 409
void main()
{
Date d1 (26, 12, 2002), d2(28,2 ,2000), d3;
d1.display();
++d1;
cout << "\nAfter adding 1 day, the date is ";
d1.display();
cout << endl;
d2.display();
d2 = d2 + 5;
cout << "\nAfter adding 5 days to the above date";
d2.display();
}
Output of the program:
Date:26-12-2002
After adding 1 day, the date is
Date:27-12-2002
Date:28-2-2000
After adding 5 days to the above date
Date:4-3-2000
Similarly we may have a counter-function that subtracts some number from the date.
This
is the same but of reverse nature. Suppose it is Jan 3rd and we have
to subtract ten days
from it. The month will be changed to December while the year is going to be
decremented by 1. To determine the date of December, we need to know the number
of
days in December and count backwards. Now we don’t need the number of days of
current month. Rather the number of days in previous month is important. Suppose
it is
3rd of March and subtract seven from it. What will be the date? Now
you have to do
complex arithmetic and take care of all the cases. It is very complicated but having
only
one time effort. Date arithmetic is very important and common in business applications.
If someone applies for vacations, you just have to enter that this person is going
on leave
from this date for ten days and you will know his date of re-joining the duty. If
someone
works on daily wages and paid after a week. Someday, he comes and says that he is
going on vacations. We need to calculate the number of days from the day of last
payment to to-date. It is simple date arithmetic. Writing a
Date class with these
appropriate operators overloaded will be very useful exercise. It adds to your overall
programming vocabulary.
There are two kinds of programming vocabulary. One is the keywords of C/C++ etc
while the second is higher-level vocabulary. What sort of vocabulary we have in
our toolbox.
In the first part of this course, we have learned how to write loops, nested loops
etc.
We learn to handle matrices and vectors using those rudimentary rules. Now if you
think
about that we have written a matrix class and a member function
inverseOfMatrix(). We
Page 410
can use this function again and again. Similarly in the
Date class, we can put
in some
rudimentary calculations on date arithmetic. Add or subtract number of days from
some
date. These are very useful functions. In the daily wages example, you need to subtract
a
date from a date. Now we need to overload the minus operator again with date minus
date. First we overload the minus operator with date minus some integer number.
There
may be two versions of minus operator. Here, you have to work in detail. Subtracting
a
date from another date is relatively non-trivial. As a programming idea, you can
think
that subtracting two dates involves huge calculations. Can we perform some logical
tests
here? If we want to implement
date1 – date2 while
date1 is smaller than
date2. The first
question is do we want to return a negative number. Let’s say we want this, then
date1 –
date2 can return a negative number. So it can return a negative number
or zero (if the
dates are identical) or positive number (the number of days). How we can implement
this
functionality? One way to do it is with the help of calendar. Under this method,
we will
start a loop till the other date is got. Then by reading the loop counter, we can
tell the
difference in days. It is a good idea. But for that, we need a calendar somewhere.
If the
dates are in different years, we will have to ensure the availability of calendar
of next
year. Think about it and try to write this function.
Now what about the plus-operator for two dates? Minus operator for strings did not
make
a lot of sense. Similarly, the plus operator for two dates does not make much sense.
We
can add some number to date. But how can we add a date to some other date. There
is no
logical and straight forward answer to this. So we don’t define such a function.
The
meaning of our operator should be obvious. You can write whatever you want in the
function. But it is bad idea. The idea of this exercise is to pay attention to detail.
Think of
all the various things that can happen. Tabulate them, determine the logic and then
start
programming. Don’t start typing your program before your brain has come up to the
same point. First analyze the problem, understand it, look at all the cases, draw
a flow
chart, write pseudo code. Once you are comfortable with this and know what you want
to
do then start writing your program. The time spending on analyses is arguably the
best
usage of your time as a programmer. The time you spend on debugging and removing
errors from faulty code is huge. Spending time on good design pays off. You should
debug for syntax errors like a semi-colon is missing somewhere. You should not face
any
logical error at debugging stage because logic errors are very hard to track. You
may just
not worry about the design and start writing code. The program may work for two
or
three cases. You may declare that you have written the program. When other starts
using
it on some other case which you did not cater, the program does not work or produces
some strange results. There is no syntax error in the program. The compiler compiles
it
successfully and makes an executable file. Now we have to check the logic. Determining
the logic from the code is a million times more difficult than determining code
from
logic. In this case, analysis will be always followed by design and then code. Please
keep
this in mind.
Unary Operators
Let’s talk about unary operators. Unary operators take one argument like
i++ or
i--(Post
Increment or post decrement operators for integers) or ++i, --i (Pre increment or
pre
Page 411
decrement operator). You can’t make unary operator as binary operator or binary
operator as unary. Let’s overload unary operator in the
Date class. We want to
overload
++. This operator should add a day in the current date. When we say ++date1
or
date1++, it should get tomorrow’s date. This is same as
date1 +=1 or date1 = date1 + 1.
We simply have to change to tomorrow’s date. If this is the member function, it
will get
the date object automatically. The internal structure of the object is available
to the
function so that it takes no argument. It will return a
Date object. Its prototype
will be as:
Date operator ++ ( ); // pre increment operator
What will be in the function definition? You have to pay attention to details. The
argument that we used in the plus operator, is also applicable here. What will be
the next
date when we add 1 to the current date. Let’s work it out. If it is not the last
date of the
month, then simply add one to the day. If the date is the last day of the month,
then
change the month and date to 1st. If the date is the last date of
the year, then increment the
year too. Suppose we have some function available which returns the days of month
given the month number. So if we say
daysOfMonth(6) it should
return 30. The function
is intelligent enough that when we say
daysOfMonth(2) it should
return 28 if the year is
not leap year. Otherwise, it will be 29. Therefore we have to send it year too along
with
the month number. We can also pass it the complete
Date structure as
daysOfMonth(date1); We will use this function in writing the ++ operator.
In a way, the
logic is same as we used in the plus operator. Suppose the object
d is calling this ++
operator as d++
where d is an object
of type Date. Therefore
the day, month and year
data members will be available to this function. In the body of the function, first
of all we
will check whether this is the last date of the month as:
if (day == daysOfMonth ( *this ) )
{
// this is the last day of the month
// process accordingly
}
In the above condition, we have checked that day is equal to the number of days
in the
month or not. If the condition returns true it means that this is the last day of
the month.
Here we have used this
to pass the object (current object) to the function
daysOfMonth.
‘this’ pointer is
implicitly available to every member function and
this pointer points to
the current object. As per requirement of the program, we have written
d++ where
d is
the object of type Date.
We are not using the object
d in the program. This
object is itself
available.
Now the data of object d
is available in the function as day, month or year. The object
d
is itself present either from its member data (day, month, year) or through the
‘this
pointer’ which points to the current object. We can also expand the definition of
the
function daysOfMonth( )
as daysOfMonth(int day,
int month, int year). If the given day is
the last day of the month, we will increment the month. Before doing this, we need
to
Page 412
check whether this is the last month or not. Therefore we have to introduce another
nested ‘if’ condition. The code segment will now be as:
if (day == daysOfMonth ( this ) )
{
// this is the last day of the month
if (month < 12)
{
day = 1;
month++;
}
else // this is the last month i.e. December
{
day = 1;
month = 1;
year++;
}
}
else // not the last day of the month
{
day++;
}
The ++ operator simply adds one to the date of the calling object. We define it
as member
function. Therefore, no argument is needed. We can make it non-member but have to
pass it a Date object.
To distinguish the pre increment operator with post increment operator, an int argument
is passed to it. The prototype of post increment operator for Date is:
Date operator ++ (int ); // post increment operator
Here we don’t need to use this int argument. The implementation is same as pre
increment operator as in both cases we want to add 1 to the date.
Can we implement the plus operator using this function? We can write the plus operator
in some new fashion. We pass it a positive integer number, which has to be added
to the
date. We can write a loop in the plus operator. The loop condition will be as
i < number
where number is
the argument passed to it. So in the program if we have written as
date1+5; the loop will run for five times and in the body of the loop
we have ++date1.
Suddenly our complicated logic has been boiled down to simple as incremented by
1.
This is the classic example of code reuse.
We don’t know who is going to use this code. Nobody is perfect. But we should think
before writing the program about the structure, interface, the setters and getters
and the
operators to be overloaded. The thumb rule is if the meaning of + and ++ operator
is
Page 413
same as in ordinary arithmetic, then + operator can be used in the ++ operator.
Keep in
mind that we can call a function from another function. This is a good example of
code
reuse. We can call the + operator as many times as needed. The
daysOfMonth is a
member function and it is used in ++ operator function. ‘+ operator’ is a member
function, used in ++ operator. We are building a hierarchy. Suppose there is some
small
logical error in the code and the
daysOfMonth is not returning
the correct value. This will
effect the + operator as well as ++ operator. When we remove that error, then +
and ++
operator both will be corrected. Moral of the story is that whenever we write some
code,
it is better to see whether we are rewriting some code in the same class. If we
are
calculating the number of months at two places or determining the leap year at two
places, then try to combine this in such a way that things should be calculated
at one
place. That piece of code may become some utility function. This will not be called
from
outside the class so we will put this function in the private area. But the member
functions can call it. We will make the
daysOfMonth() as a private
member function of
the class. It will return the days of the month having checked whether this is leap
year or
not. Using this utility function, we have written + and ++ operator function. Don’t
repeat
code inside a class. Make it a general rule. Make a function for the repeated code
and call
it where needed. For efficiency and speed, we can repeat the code. For this, we
start using
macros. It means that if you put all your logic in a single place and then reuse
it. You will
get lot of safety and security with this. A correction at one place will make the
behavior
of the whole class correct.
Let’s see another interesting function of the
Date class. Sometimes,
we need to compare
two dates i.e. whether a date is greater or less than the other date. In other words,
the
comparison operator is applied. Comparison operators <, >, <=, >=, == can also be
overloaded. How do we determine whether
date1 is greater than
date2? First of
all, what
will be its return type. Return type has to be either true or false. It says
date1 is greater
than date2 or
date1 is not greater
than date2. Let’s
introduce another keyword bool.
It is a
new data type. It is very simple, it only takes two values true or false. So, the
return type
of greater than operator (>) is
bool. The prototype of
this member function is as:
bool operator > (Date d);
The argument d is
the Date object
that is on the right side of the greater than sign. The
left hand side Date
object is available to this as this is the member operator of the class.
Before writing the code, think abut the logic. We have to determine that the calling
date
is greater than the date d
or not. If the year of current date is greater than date d, will the
current date greater than date
d? Certainly, it will be
so. If the year is greater, obviously
date is greater. If the years of both the dates are equal, then we have to check
whether the
month of the current date is greater than date
d or not. If the month
of the current date is
greater than the date d,
current date is greater. If the months are also equal, we will
compare the days. It’s a very simple hierarchical logic. To be able to write this
logic
cleanly, you should write case by case on paper. Analyze it thoroughly. The logic
can be
written in reverse too. If the year of the date
d is greater than the current
date, return false
and so on. So we can go either true, true, true or false, false, false logic. You
will find the
false logic quicker. We can use if, ‘else if’ structures. Return type of this function
is
Page 414
‘boolean’. Suppose that in our calling function we have two
Date objects as
d1 and
d2.
We will write as if(d1 > d2).
Why should we write this? As our operator is returning true
or false and ‘if’ also needs true or false, we can write very clean and neat code.
This
greater than operator is a member operator of
Date class. In this case,
we have the return
type as boolean and not returning the
Date object. Is it the
violation of any rule? The
answer is no. The return type can be anything. It needs not to be the same as the
class. It
can be anything. The same applies to difference between two dates. The difference
between two dates will be an integer. It is still a member function. It simply tells
us the
number of days between two days. It could be negative or positive but it is an integer.
There is no such rule that the member operators should return the object of the
same
class.
The code of the greater than operator is as follows:
// Definition of the greater than operator
bool Date :: operator > ( Date d )
{
if ( year > d.year ) // if year is greater date is greater
{
return true;
}
else if ( year == d.year) //if years are equal check month
{
if ( month > d.month ) // if month is greater date is greater
{
return true;
}
else if ( month == d.month) // if months are equal check dates
{
if(day > d.day)
return true;
else // otherwise return false
return false;
}
else
return false;
}
else
{
return false;
}
}
Now you can write all the comparison operator of the
Date class. The comparison
operators are greater than, greater than or equal to, equal to, less than, less
than or equal
to. If you are writing one, you might want to write all of them. Now we have expanded
Page 415
the Date class enough.
As an exercise, write your own
Date class. Keep in mind
the
principles. What should be in the
Date class? How should
we set its values? How many
constructors we need? Do we need a destructor or default destructor is enough. After
this,
define its public interface that is the member functions that are visible from outside.
What
operators should be overloaded?
In the program, if we say date1
+ 5, we know that we will get a date which is five days
later. What will happen if we write
5 + date1? The situation
like this may happen. You
have published your Date
class and someone wants to use it in this fashion. Here we have
an integer, the plus operator and a
Date object. It should
return an object of type Date.
To
make this work properly, we need to have another operator. You will have to look
at the
set of operators needed for this class. List them out and write down their behavior.
Be
very clear what you expect them to do and start writing the class. How can we implement
integer + date? On the left hand side, we have an integer. If the integer
is at the left side,
it can’t be a member function. Member function is always called by object. Here
object is
not calling the function. Rather integer is calling. So it has to be a friend function
that is
sitting outside. It will get two arguments, integer and
Date. As this is the friend
function,
the internal structure of the
Date will be available to it. You will create a new
Date object
based on the given Date
object and the integer and return it. We have seen that member
functions are returning integers or Boolean. Here, a non-member function is returning
an
object of Date class.
When we have listed out comprehensively that what will be the
interface of our class. Which functions and operators will be visible from outside?
When
we have written the behavior of our class on paper, it is good to start writing
the code.
You may have to write a lot of code for this class. Once we have compiled the code
and
have object file, then anyone can use
Date object. There will
be no problem. We will
include the “date.h” file in the program and use and manipulate the
Date objects. We can
use its operators, member functions etc very easily. The effort we put in writing
this code
does not go waste. It will provide a lot of ease in the main program. The biggest
advantage is the encapsulation that has happened. All of the logic that was needed
to
manipulate the object of class
Date is now encapsulated
in that class. In case of any
problem in the behavior of the class, we will need to correct the class, compile
it. In the
conventional function programming or structured programming, this logic has been
split
at different locations of the program. It was everywhere. Different things have
been
embedded at different points. In the function oriented programming, we have written
a lot
of functions at different locations. Here we have a new data type as
Date. All the date
related functions are at one place. We are encapsulating all the functionalities
in the Date
class that is another reason for doing all of the homework, all the thinking before
we
write the code. No one can determine all the usage of the
Date class. If you start
determining all the usage of
Date class and writing the definition of the
Date class for the
last six months, this will be impractical. You would want to keep it within limits
but do
the homework then you write it. Now you can reuse it as many times as you want.
We need a friend operator when the driving thing is not the object of the class
like integer
+ date. The operator is derived by integer. Here, we use a friend function.
There are
Page 416
instances where friend operators are used to manipulate two different classes. A
classic
example in the mathematics is of the multiplication of vector and matrix. If we
have to
multiply a vector with a matrix, the multiplication operator will be friend of both
the
classes. It will get both vector and matrix as arguments and manipulate them. Keep
in
mind that friend operators in a way can also be used to glue two classes. The
disadvantage of clubbing them together is that they become interlinked. Write your
own
overloaded operators and see how they work. |
|
|
|