|
|
Summary
1) Classes And Objects
2) Constructors
3) Types of Constructors
4) Utility Functions
5) Destructors
Classes and Objects
This lecture is a sequel of the previous discussion on 'Classes' and 'Objects'.
The use of
'classes and objects' has changed our way of thinking. Instead of having function-oriented
programs i.e. getting data and performing functions with it, we have now data that
knows
how to manipulate itself. This way, now the programming is object-oriented. It means
that our programs revolve around data and the objects. Therefore, it would be nice
to
have some building blocks for programs so that these can be combined to write a
program. We have so far talked about simple variables like integer, double and char,
followed by strings of characters and arrays of different data types. But now, in
the form
of an object, we have a block which knows itself about contents and the behavior.
The
upcoming discussion will further explain it. We have used
cout for displaying many
things like integers, doubles and strings. Here integer did not know how it is going
to
display itself. However, cout
knows how to display an integer on the screen. Now we
want to see that an integer should know how to display itself. So it will be a different
process. Now the question arises whether it will be good to see an integer knowing
how
to display itself? For this purpose, we will have to expand the scope of thinking.
While engaged in the process of programming, we try to solve a real-world problem.
The
real world is not only of integers, floats, doubles and chars, but there are other
things like
cycles, cars, buildings, schools and people. We perceive all these things as objects.
Each
object has a behavior associated with it. Consider the example of a man who can
talk,
walk, sit and stand etc. Similarly, we can think of a vehicle that has many functions.
Page 337
These objects also have attributes. For example, a man has height, weight, color
of eyes
and hair and so on. These all are his attributes. His actions will be referred as
functions or
methods. This principle may be applicable to vehicles, aero planes and all other
realworld
things. An aero plane has attributes like its height, width, number of seats and
number of engines etc. These are attributes called data members. Its actions include
takeoff, flying and landing. These actions are its functions or methods.
In terms of language, the attributes and actions may be equated with nouns and verbs.
The verbs are actions which are also called methods in the programming terminology.
These methods should be included in the object in such a way that the object itself
knows
how to achieve this function or method. Now consider this all in terms of data.
Is it
always in terms of salary, payroll, amount and numbers? Actually, data comes in
different varieties. Now a day, a computer is multimedia equipment. We see that
there are
not only alphabets and digits displayed on the screen, but also pictures, images,
windows,
dialogue boxes, color and so many other things. These are numbers, letters and graphics.
Other than this, we can see videos on our computer. It is just another type of media.
Similarly, we find audio material, which can be played on the computer. Thus in
the
expanded terms of data, we come across numbers, pictures, audio and video while
dealing with multimedia.
Now we think about the concept that an integer should know how to display itself.
With
the enhanced scope of data, we can also have an audio, which knows how to play itself.
The same applies to video.
Class
A class is a way of defining a user-defined data type. In a class, one may find
data
members and functions that can manipulate that data. In the previous lectures, we
have
talked about the concept of data hiding i.e. encapsulation that means that the data
of a
class cannot be accessed from outside. However, it can be done through some defined
functions (methods). These are the member functions of the class. To hide the data,
we
declare it private.
If a data is private, it will be available only to member functions of the
class. No other function outside the class (except friend functions) can access
the private
data. Normally in a class we divide the private part which is normally what we called
implementation of the class, from the functions that manipulate that private data
which is
called the interface (which is the front end).
The example of a room can help us understand private and public parts of a class.
A class
is a room having a curtain in its middle. The things behind the curtain (private)
are visible
to the residents (insiders) of the room. They know about every thing present in
the room.
When the door opens, the outsiders see only the things in front of the curtain.
This is the
public interface of the class while behind the curtain is the private interface.
A function
inside the class (i.e. a member function) can access and manipulate all things in
the class.
A function outside the class can only access and manipulate its public interface
part. A
constructor has to be in the public section of the class. There should also be a
public
interface so that it can be called from outside.
Page 338
Constructors
Constructor is a special function, called whenever we instantiate an object of a
class. If
we do not define a constructor function in a class, the C++ provides a default constructor.
It is executed at the time of instantiating an object.
To understand the basic function of constructor, we have to go back. While writing
c++
Stroustrup noticed that the majority of programming problems, which we call bugs,
occur
due to the use of uninitialized data. That is, we declare variables and use them
without
providing them any value. For example, we declare an integer as
int i ; And it is not
initialized with a value like
i= 0; or i = 5; And then somewhere in the program, we write,
say, j = 2 * i ;.
This is the usage of an uninitialized data variable. This technique has a
demerit that despite having no syntax error, it may cause a logical error, which
is difficult
to find. Thus, initialization of data is a very critical activity. The constructors
give us an
opportunity to initialize the data members of an object in such a way that when
our
program gets an object, the data part of the object is in a known state. Being in
a valid
state, it can be used. The constructors are used to initialize the data members
of an object.
A class is a user defined data type it does not take space in the memory unless
we create
an object from it. The constructors create space for data members and put values
in them.
We want these values to be there when an object is instantiated. Thus initialization
is a
good reason for using constructors.
Types of Constructors
Compiler Generated Constructor
If a constructor is not defined by the use the compiler generates it automatically.
This
constructor has no parameter. It does nothing. Although the compiler will create
a default
constructor for us, the behavior of the compiler-synthesized constructor is rarely
what we
want. Thus the default constructor provided by the compiler does no initialization
for us.
Simple Constructor
We have earlier discussed that we can write a constructor that takes no argument.
The
user defined constructor, that takes no argument is called a simple constructor.
We know
that when a compiler generated default constructor is called, it does no initialization.
It
does not know whether to put a value in data members like day, month in our previous
class Date. We can avoid this problem by not writing a class
without having its
constructor.
A simple constructor can do initialization without any need to take any argument.
So we
can write a constructor of Date class like Date ();.
When we write such a constructor, it
automatically assumes the roll of the default-constructor. The compiler will not
call the
default constructor. Rather, the constructor written by the programmer will be called
whenever an object will be instantiated. It is also a good programming practice
to provide
a default constructor (i.e. a constructor wit no argument).
Parameterized constructors
We may define a constructor, which takes arguments as well. This constructor will
be
automatically called when the required number of arguments are passed to it. Through
Page 339
this, we can easily assign the passed values to our class data members for that
particular
object.
In our previous example of class Date, we have written a constructor
as follows
Date (int, int, int);
This is a parameterized constructor which takes three arguments of type int.
Constructor Overloading
We can provide more than one constructors by using function overloading. The rules
for
function overloading are that the name of the function remains the same. However,
its
argument list may be different. There are two ways to change the argument list.
It can
either vary in the number or type of the arguments. We cannot have two functions
with
the same number and type of arguments. In such a case, these will be identical.
So it will
not be function overloading. The function overloading requires the argument list
to be
different. The same concept of function overloading applies to constructors. If
we supply
more than one constructor for a class, it can be called one or the other depending
on the
way of calling.
As the constructor does not return any thing, so it has no return type. It means
that the
body of the construct function cannot have any return statement. Otherwise, the
compiler
will give a syntax error.
Explanation of Constructors
The main purpose of the constructor is to initialize the object in such a manner
that it is in
a known valid state. Consider the example of Date class again.
In that example, there
were three data members i.e. day, month and year of type int. What values will we
give to
these data variables by default if we create an object of Date?
There may be any valid
date. We can give a value 01 to day, month and 1901 to year or what ever we want.
It
will be a known state despite being meaningless. We can write a constructor of class
Date which takes three arguments int day, int month and int year,
and puts values in the
data members of the object, being created. Now the question arises when does this
happen? It happens when we instanciate an object of class Date
by writing Date
myDate; When this line executes in the program, some space for 'myDate'
is reserved in
the memory. This space contains the space for day, month and year variables. Then
control goes to the constructor that assigns values to day, month and year. Being
a
member of the class, the constructor can write values to the data members of the
class
that is private. .
In C++ language, we can provide default arguments to functions. As a function, the
constructor can take default arguments. Suppose we have written a constructor of
class
date with the arguments by providing default values to its arguments. We can write
a
constructor as Date (int day=1, int month=1, int year=1);
and create an object of class Date as
Date myDate;
This creates an object of type Date. Which constructor will be called? A constructor
with
no arguments or the parameterized constructor in which each argument has given a
value? If we provide a constructor which has default values for all the arguments,
it will
become the default constructor for the class. Two constructors cannot be considered
Page 340
same. So it will be better not to write a constructor Date (); (constructor with
no
argument) in case of providing a fully qualified constructor (with default values
for all
arguments).
Now suppose, we want to initialize the Date object properly
by passing a character string
to its constructor. Is it possible to write such a constructor? Yes, we can write
such a
constructor. This constructor will take date as a string, say, 01-jan-1999. And
in the
constructor, we can split up this string with 'string manipulation functions' and
assign
respective values to day, month and year.
Now we recapture the concept of constructors with special reference to their
characteristics.
A constructor is a function which has the same name as the class.
It has no return type, so it contains no return statement.
Whenever an instance of a class comes into scope, the constructor is executed.
The constructors can be overloaded. We can write as many constructors as we require.
At one time, the compiler will call the correct version of the constructor".
Utility Functions
The second issue, we usually come across while dealing with the concepts of 'classes
and
objects' is that a class has a data on one side (normally private part) and functions
on the
other (normally public part). The functions (methods) are normally written in public
part
of the class. Are there functions which are private to a class? Answer is yes. The
functions of a class may be of two categories. One category contains the member
functions which manipulate the data or extract the data and display it. Through
these, we
can set and get values to manipulate data. These are the functions which are in
public
interface of the class and manipulate the data in the object. But sometimes, we
need such
functions that is the requirement of these member functions. Suppose we write a
setDate
function. This function is given an argument and it does the same thing as done
by the
constructor. In other words, it sets a value of date. Now that function can be public
so
that it can be called from outside the class. Now we want that the member functions
of
the class can call this function. But it should not be called from outside. In this
case, we
put this function in private section of the class. These functions are called utility
functions. These are a utility used by other methods of the class. However, they
are not
functions, supposed to be accessed from outside the class. So they are kept private.
Destructors
The name of the destructor is the same as that of a class with a preceding tilde
sign (~).
The ~ and name of the class is written as a single word without any space between
them.
So the name of the destructor of class Date will be
~Date. The destructor can not be
overloaded. This means that there will be only one destructor for a class.
A destructor is automatically called when an object is destroyed. When does an object
gets destroyed? When we create an object in a function, this is local to that function.
When the function exits the life of the object also comes to end. It means that
the object
is also destroyed. What happens if we declare an object in the main program? When
the
main program ends, its objects also comes to end and the destructor will be called.
Page 341
The destructor is normally used for memory manipulation purposes. Suppose we have
such a class that when we create an object of it then its constructor has allocated
some
memory. As we know that we have to free the allocated memory to ensure its utilization
for some other program. The destructor is used normally for this purpose to make
sure
that any allocated memory is de-allocated and returned to free store (heap).
The destructors can be summarized as the following.
The destructors cannot be overloaded.
The destructors take no arguments.
The destructors don’t return a value. So they don’t have a return type and no return
statement in the body.
Now we come to the previously defined class Date. Let's see
what can we further put in
it. We have put in it constructors. We have provided a parameterized constructor
without
default arguments. So the constructor with no arguments will become default one.
We
have another constructor with three parameters so that we can pass it the values
for day,
month and year. There is also provided a destructor. We have written some methods
to
set day, month and year. These were setDay, setMonth and
setYear respectively. Each
one of them takes one parameter, a simple integer. Then we have get functions. The
functions getDay, getMonth and
getYear return a simple integer. There is also a
function setDate, which takes three parameters (i.e. day, month
and year) and sets them.
In set function, we do not simply assign the values to the data members. This can
be done
through a constructor. Whenever we put data into an object, it is necessary to make
it sure
that valid values should be stored. For example, if we say Date myDate
; and give it
values like 35 for day, 13 for month and 2000 for year. The constructor will set
these
values. But these are invalid values for a date. Here we want that these values
should be
validated before being assigned to data members. So we write some code for error
checking of the values and store only valid values in data members i.e. day, month
and
year. We do the same thing in set function. Then what is the advantage of using
set
functions. The set functions are public part of the class and can be called from
outside the
class and also by the constructor. So write all the code for error checking and
to validate
the data in set function and call this set function in the constructor. Thus when
we create
an object of class date, it is written as the following
Date myDate (12,10,2000);
Then an object of Date class is created and the constructor
of the class that takes three
arguments, is executed by passing these three values. In the constructor, we call
the set
function which sets the values of the data members properly. Thus we get a fine
initialization of the data members.
What an Object is ? An object is an instance of a class. When we say an instance
that
means that this object exists and takes space in the memory. What happens when we
create an object i.e. take an instance of the class. A class contains data and methods.
Are
these methods reproduced for every object? Every object has data of its own as every
object is distinct from the other. For example, in case of the date class, there
may be
objects date1, date2 and date3. These are three different objects having their own
value of
Page 342
date. Being distinct objects, they must have distinct space in memory. What about
functions inside the class?
Whenever we create an object of a class, the functions of the class take a space
in the
memory and remain there. There is only one copy of these functions in the memory.
The
data part of the class takes individual locations in the memory. So if we create
three
objects of a class, say date, there will be one copy of the functions in the memory
at the
time of execution of the program. The data will have allocated three spaces in the
memory with different values. Now suppose, we want to change the data of date1,
there
is need of setting month of date1 to 3. So we call
setMonth function for the
object date1.
We use dot operator (.) to call the function of an object. We write this as
date1.setMonth(3); The
setMonth function is called
from the copy of the functions that
is in the memory. The object name with dot operator makes sure that the function
will
operate on the data of that object. Thus only the value of the month of date1 will
be set to
3. The values of date2 and date3 will remain untouched. Similarly if we say
date2.setDay(23); the
setDay function will be
called for object date2 and day of date2
will be set to 23. Thus it is clear that which object calls the function the data
of that object
is visible to the function and it manipulates only that data. Thus we have not wasted
the
memory by making separate copy of the functions for each object. All objects of
one
class share the common functions. On the other hand, every object has its own data
space.
The overloaded functions and constructors are also found in this single copy and
called
whenever needed. In the overloaded functions, the appropriate function to be called
is
resolved by the parameter list (type and number of the arguments to be passed).
In our class Date, we need no functionality for the destructor.
We write the destructor
~Date and a cout statement in it. That displays the message ‘The
object has destroyed’
just to demonstrate the execution of the destructor. Similarly we can display a
message
like ‘Date object created’ in our constructor function. By this, we can see when
the
constructor is called. By seeing these messages on the screen we know that the object
is
being created and destroyed properly. If the constructor function is overloaded,
we can
put appropriate message in each constructor to know which constructor is called
while
creating an object. For example in default constructor, we can display a message
‘Default
constructor is called’.
The following program demonstrates the execution of constructors and destructors.
It is
the previous example of Date class. It displays appropriate messages according to
the
constructor called. You will see that the constructor is called depending upon the
parameter list provided when the object is being created.
/*
A sample program with the Date class. Use of constructors and destructor is shown
here.
A message is displayed to show which one constructor is called
*/
#include <iostream.h>
//#include <stdlib.h>
// defining the Date class
class Date{
// interface of the class
Page 343
public:
void display(); // to display the date on the screen
void setDay(int i); // setting the day
void setMonth(int i); // setting the month
void setYear(int i); // setting the year
int getDay(); // getting the value of day
int getMonth(); // getting the value of month
int getYear(); // getting the value of year
// Constructors of the class
Date();
Date (int, int);
Date(int, int, int);
// Destructor of the class
~Date ();
// hidden part of the class
private:
int day, month, year;
};
// defining the constructor
// default constructor. setting the date to a default date
Date::Date()
{
day = 1;
month = 1;
year = 1900;
cout << "The default constructor is called" << endl;
}
// Constructors with two arguments
Date::Date(int theDay, int theMonth)
{
day = theDay;
month = theMonth;
year = 2002;
cout << "The constructor with two arguments is called" << endl ;
}
// Constructors with three arguments
Date::Date(int theDay, int theMonth, int theYear)
{
day = theDay;
month = theMonth;
Page 344
year = theYear;
cout << "The constructor with three arguments is called" << endl;
}
//Destructor
Date::~Date()
{
cout << "The object has destroyed" << endl;
}
// The display function of the class date
void Date::display()
{
cout << "The date is " << getDay() << "-" << getMonth() << "-" << getYear() <<
endl;
}
// setting the value of the day
void Date::setDay(int i)
{
day = i;
}
// setting the value of the month
void Date::setMonth(int i)
{
month = i;
}
// setting the value of the year
void Date::setYear(int i)
{
year = i;
}
// getting the value of the day
int Date::getDay()
{
return day;
}
// getting the value of the month
int Date::getMonth()
{
return month;
}
// getting the value of the year
int Date::getYear()
{
Page 345
return year;
}
/* Main program. We will take three date objects using the three constructors
(default, two arguments and three arguments and display the date.
*/
int main()
{
Date date1, date2(12,12), date3(25,12,2002); // taking objects of Date class
// displaying the dates on the screen
date1.display();
date2.display();
date3.display();
}
Following is the output of the above program.
The default constructor is called
The constructor with two arguments is called
The constructor with three arguments is called
The date is 1-1-1900
The date is 12-12-2002
The date is 25-12-2002
The object has destroyed
The object has destroyed
The object has destroyed
|
|
|
|