|
|||||
CS201
Introduction to Programming
Lecture
Handout
Introduction
to Programming
Lecture
No. 42
Reading
Material
Deitel
& Deitel - C++ How to
Program
Chapter.
12, 20
12.4,
12.5, 12.7, 12.8,
20.1
Summary
68)
Class
Templates
69)
Class
Templates and Nontype
Parameters
70)
Templates
and Static Members
71)
Templates
and Friend Functions
72)
Example
73)
Sample
Program
74)
Advantages
and Disadvantages of
Templates
75)
Standard
Template Library
As
discussed earlier, template functions are
utilized while writing functions
for generic
data
type. We take benefit of templates in
case of writing the same
function repeatedly. In
this
case, the writing code
seems very similar, in fact
identical. But the data
type is
changed
for different versions. So we
write a function for generic
data type whose syntax
we have
used as under
template
<class T>
Here T is a
generic data type. Now in
the function we write T
while dealing with a
data
type.
This becomes a generic template of
the function. During the
process of using this
template,
this function with a particular
data type is called. The compiler
automatically
detects
the type of the data
passed (say we passed an
int) and generates a new
copy of the
function
with int. Here T is
written in the original
template. The copy is compiled to
the
object
code while existing in the
program. The same thing
applies to other data
types,
used
for the same function.
Thus, we create a family of functions
with a single template.
The
functionality of these functions is the
same but with different
data types. For
example, if we
want to find the square of a
number, a template square will be
written
first. It
will be called with int,
double or float. Otherwise, we have to
write the over
loaded
versions of the square
function for different data
types. So template functions are
of good
use for the programmers.
They promote code reuse. The
major advantage of their
Page
541
CS201
Introduction to Programming
use is
that once we write correct
code with correct logic
for a template function,
there
will be
no need to re-write it. How
can we test a template? We
write the template in
reverse
technique at the moment. When it is known
what the template has to
do, we take
a data
type for example int. A complete
function for int is written
and tested. After
the
ascertainment
of the accuracy of function's
working, the int
in
the function is
replaced
with T
and declared a template by
writing template
<class T>. We
cannot see the
code
generated
by the compiler in the editor. So it
becomes difficult to debug
the template
code. We
have to read and check
the code carefully while
writing it.
After
having a detailed discussion on the
template function, we will
now talk about
template
classes.
Class
Templates
Creation
of a data type of our own
with the same behavior
for int, float
and
double
etc,
is
the
case of defining a complete interface
and implementation in a generic
fashion. To
further
understand this concept,
let's talk about a data
structure called stack. We will
now
try to
understand how does stack
work, what its properties
are and can we make
it
generic.
You
might have seen the
plates, kept together in a
orderly manner i.e. one on
the other.
This is a
stack. Now if someone wants to
add a plate on the pile, he
will have to put it
on
the
top of the stack. So,
there is only one way to
add something on the stack.
Similarly, if
we want
to pick a plate from the
pile, it will be taken from
the upper-most tier. Thus
the
property of
the stack is that the
last placed thing will be
picked first. This phenomenon
is
called `Last-in,
first out' or LIFO. In programming, we
can understand what thing
we
want to
add, the required thing is
added to the top of the
stack. When we pick up a
thing
from
it, the last placed
item is picked first. Following
this rule of stack (last in first
out),
we can
make a stack of integers, floats
and doubles etc. Here, the
stack is a class with
a
defined
behavior, interface and the
data, it holds. Now we say
that the data held by
the
class is
variable to help make a stack of
integers, floats or doubles. Thus,
stack is a good
candidate
for a template class. It
means that when we
instantiate the class for
creating
objects,
a stack of integers or floats is
required. The behavior of
the compiler in
template
classes
is the same as in template functions. If
we want to instantiate a template
class
with a
new data type, the
compiler will generate a new
version of the class with
the
specific
data type at the place of T in
the template class.
We know
that a class is a user-defined
data type. With the
help of a template class, we
make
another class of the user
defined data type. In other
words, things are not
restricted
to
creating copies of class
only for native data type.
Copies of class of our own
data type
can
also be created. It is a case of a
real extensibility.
Let's
see the syntax of this
generic template class. It is
similar to the simple
template
function
in which we write template
<class T>. Here T is
the placeholder that will
be
replaced
by the data type when we use
it. The syntax of the
template class is
template
<class T>
class
class-name()
{
definition
of class
Page
542
CS201
Introduction to Programming
};
In the
definition of the class where
the generic data type is
required, we write T.
For
example,
there is a class in which we
want to write int
data
type. The int
is
the data type
that
may be a float
or
double
at
different times. For this, T is written
wherever we are
using
int
in
the class definition. Be careful
that some times, we may use
integers as it in a
class.
For example, when we create a vector or
array, its size will be an
integer. But the
array
will be of integers, floats or chars. So
don't confuse it them. It is better to
replace T
whenever
necessary.
We start
writing of a template with
the following line
template
<class T>
Later,
definition of the class in ordinary
fashion begins. We should use T wherever
in
case of
employing the generic data
type. T is not something fixed
for this purpose. We
can
use a, b or c or whatever needed.
However, T is normally
used.
The
member functions are normally defined out
side the class. To define
the member
functions of
the template class, we
write
template
<class T>
class
name <T>::function
name (argument
list)
{
//
function body
}
In the
function body, the programmer will
write T wherever it is needed. This
way, the
template-class
and its member functions are
defined. However, when we
use the class in
main
program or other function, the
objects of this class are
declared. Suppose there is
a
class
Number, say Number
x; As
Number is a template class, we will
have to tell the type
of the
number. Let's see the
code of this simple template
class Number. The
Number
class
can store and display a
number. The definition of
the class is written as
under.
template<class
T>
class
Number
{
private:
T
myNumber;
public:
Number( T
n );
display();
};
We create
an object of this class in main
program by telling the type
of the number in the
following
way
Number
<data type>
Page
543
CS201
Introduction to Programming
Here data
type may be int, float
or
double. Now we
can create an object of this
class for
an
integer type as
follows.
Number
<int> x ;
We can
read the line as x
is an
object of class Number and
its parameter is an int.
The
way of
analyzing it is that wherever we wrote T
in the class, now int
is
written there.
Compiler
does it automatically. We will
not see the code of
this class written for
int.
The
compiler
generates a copy of the class
for int
and
uses it. Similarly, if we
write
Number
<double> y ;
Here y
will be
an object with the data member of
type double. Again,
the entire copy of
the
class will be created with
double
everywhere
instead of T. The program will
compile
and
run. So it is quiet useful and a big
shortcut.
Class
Templates and Nontype
Parameters
There is
a little variation, which is we can
also use non-type parameters
in templates.
What do
non-type parameters mean? We
have been writing template
<class T>.
However,
while writing template
<class T, int element>, the
non-generic type (i.e. int)
will be
treated as a constant in the
class definition. In the
class definition, wherever we
use
the name element, it will
be replaced by the value
passed to it. Arrays when
declared
and given
a number in square brackets,
the number will be a
constant. Similarly,
while
using
with the # sign, we associate a
name with a number which is
a constant. Here the
non-type
parameter in a way behaves
like a constant. We can use
it to give a dimension to
the
things. Instantiating a class, we not
only replace T but also
provide a value for
the
non-type
parameter defined in the class
definition.
By using
templates, we save a lot of
effort and labor. The other
big motivating factor is
the
reuse of tested and tried
code for a particular problem.
Templates
and Static
Members
Now
let's talk about the
implications of the template classes. To
understand the
behavior
of
templates with static members, we
have to comprehend the
concept of static member
variables.
Static variable members are used to
define ordinary classes. The static
variable
has a
single copy for the whole
class. So there are not
separate copies of the static
data
variable
for each object like ordinary
data members.
Now
let's see what happens
when a static member is a part of a
template class. The
instantiation of
the class has two
parts i.e. one is creating an object
while the other is
the
type of
the object. For example, from
the previous class Number, we
write
Number
<int> x ;
Page
544
CS201
Introduction to Programming
Here x
is an
object of generic class Number
with a specific type int. We can
also use float
or double
instead
of int. We
suppose, there is a static variable in
the Number
class.
On
instantiating
the class with int, there
will be a copy of static variable for int
set-off
objects.
If we instantiate the class
for float, there
is going to be a copy of the static
member
for float numbers. So the
member is static (i.e. there is one copy)
for that type of
the
objects. There will be one
static value for different object
created with type int
while
another
static value for different
objects created for type double. So,
this static value is
not
class wide. It is something of specific
nature. In simple words, the
compiler reads the
code of
program (main or other function)
and generates a copy of the
template class
accordingly. It
also gives a name of its own
to this copy. Thus in a way,
the compiler
generates
a new unique class, replacing T
with int
(or
any other data type we
want). The
static member of
this unique class behaves
exactly like the ordinary
class. Similarly the
compiler
generates a copy for double
with a
unique name. Here the static member of
this
copy will
affect the objects created
for double
data
type. The static variables
are
instantiated
once for each type whenever
we instantiate a class while replacing
generic
data type
with a specific data
type.
It is
pertinent to note that we
can replace the generic
data type with our own
data type.
This is
slightly tricky. Suppose, we
have written a class, `Person'. There
is also a generic
class
Array, which can be
instantiated with int, float
or
double
data
type that means it
may be an
array of integers, floats and
doubles respectively. Can we do so with
an array
of
persons? If we have defined a
class called Person, there
may be an array of Person.
Person
now
behaves like another data
type. At the moment, it does
not matter whether
the
data type is user defined or
not.
We have
to be careful that when we are
using our own object i.e.
our own class in a
template,
it must support the functions and
interfaces, needed for this
generic structure of
the
class. So don't put in something
that cannot be used by this
generic structure. We
have
discussed an example of phoneCall where reverse
returns
x
by
converting it to x.
In
that
example, we had to define the minus
(-) operator for phone
call. Similarly, in
that
example,
billCode is changed to `c'. If
number is passed, the
negative number will
be
returned.
Its behavior was changed in phoneCall. So
we have to take care of
these things.
Whenever
we use a template class and
instantiate it for one of
our own classes, it
is
necessary
to have compatible function calls in it.
It means that member functions
behave
properly as
per requirements.
Templates
and Friend
Functions
Now we
will have a look on another
concept i.e. friend functions. We have
read in
classes
that a programmer can declare functions
as friend of the class.
Let's first look at
the
need of friend functions. Suppose we
have defined an operator for
our class, say +
operator.
We know that + is a binary operator
that takes two arguments. It
implements a
+ b.
While implementing + operator
for a class, we see that
the calling object is on
the
left
side of the operator (i.e.
a). The + operator gets
this calling object through
this
pointer. It
has an argument on the right
hand side i.e. b. Here,
a
is an
object of our class
and
b,
an
ordinary data type. Similarly, we
have a + 2 ;
where
a
is an
object of a class
Page
545
CS201
Introduction to Programming
and 2, an
ordinary int. Now
this + operator has to
behave intelligently. This
way, we have
over
loaded it within the class
definition. What happens
when we say 2 + a ;
? In
case of
ordinary
integers, 2 + 3 is same as 3 + 2. So we
want the same behavior in a
class. We
want
2 + a
behaving
the same way as a+
2. We
cannot carry out overloading of a
member
function for this. When we write
2 + a;
there
will be an int
on
left- hand side of
the +
operator. It is not a member function of
the class. For a member
function or
member
operator, the object on left-
hand side should be that of
the class. So if we want 2
+ a
; we
have to write a friend
function for it. Here, the
private data of the class
is
manipulated by this
function. For this purpose,
there is need to have access
of this
function
to the private data of the
class. The only way
through which an external
function
can
have access to the private
data of the class is declaring
that function to be a friend
of
the
class. So this was the
motivation of the friend
function.
Now
what will be the behavior of
friend functions in a template class.
For example if we
write in
our template class
friend f
();
Here f
is
function name. Thus in above
statement, we say that f
is a
friend function of
the
class. It
is declared in a template class.
While creating an object of a template
class, we
tell
the type ( int, float
or user defined data type
etc) of
which the class is to be
generated.
Now
when we have written friend
f(), f
becomes a
friend function for all
classes
generated
by using this template. So it is
very global that means
f
will
have an access to
the
private data structure of any
and all different classes
which are instantiated by
this
template
class.
Now we
write T in the argument list
of the friend function. If we
have instantiated a
class
for an
integer, and we have written
the friend function f
with T as
f
<int>. This
will mean
that
this function is a friend
for all int
versions
of this class. This is an
interesting
concept.
If we have a double
version of
the class, this f
(i.e.
f<int>) will not be a
friend of
it. It is
only a friend of the integer
versions of the
class.
Similarly,
if we have a friend class
declared in the template
class as
friend
class Y;
then it
means that all the member
functions of the class Y
can
access the private data
of
any
class-type generated with
the help of this template.
For example, if we generate a
class
for int, the
member functions of class Y
can
handle the data structure of
the object of
this
class of type int. If we
instantiate a class for double
from
this template, the
member
functions of
Y
can
handle the data of the
object with double
version.
Similarly
if we write in a template class
friend A
:: f ()
Page
546
CS201
Introduction to Programming
Here f
is a
function. It means that the
member function f
of a
class A
is a
friend of this
template
class. We have not granted
access to the whole class,
but only to a function
of
that
class. That access applies
to all classes generated
using this template.
Finally,
if we use <T> with the
function f, it
becomes specific. In other
words, this friend
function
(i.e. written with <T>)
will be a friend of classes
generated by the template
with
T data
type. It will not be a
friend of the other versions
of the class. Here T may be
int,
float, double
or
any user defined data
type.
Example
Let's
apply these concepts on one
specific example. We may create a
class called Stack
in
a generic
fashion. There are some
properties of the stack.
Firstly, we should be able
to
know
that at what position in the
stack we are at a particular time. So
this is a concept of
stack
pointer. We take an array for
stack. The stack pointer
always points to the top of
the
stack. It
will be good to make the
array generic so that we can
make an array of any
data
type.
Then there are few
questions like is stack
empty or full etc. Here the
code seems
fairly
straight- forward. We start
with
template
class <T>
and
then write
class
Stack
In the
class definition, An integer variable
called size
is
declared for the size of the
array.
Then we
declare the array and
write its data type as T,
which is a generic type .It
will be
replaced
by int, float, double
or
char
when we
will use this array. We
can use the
dynamic
memory allocation for the
array. But we use a fixed
size array for the sake
of
simplicity.
To declare an array, we need a
constant value. Normally,
this constant value
is
not
written in the class
definition. It will go to the
constructor and be required
when the
constructor
will be called for an object. We can
use the constructor to
actually define the
array
for us. We need some
utility functions. The function
push()
is
used to push an
element
on the stack. We use the
function pop()
to
get an element from the
stack. The
push()
and
pop()
functions
put and get things of type
T. So pop()
should
return something
of type T.
That means it will return
int
if
int
is
pushed and returns double
if
double
is
pushed
and so on. So we need push()
and
pop()
which
are parameterized with T.
After
this,
there is need of some functions
for generic manipulation
like if stack is full or
if
stack is
empty. We can write function
isempty() that
returns a
Boolean. If it returns
TRUE,
the stack will be empty.
However, presence of something in the
stack will turn it
FALSE.
Similarly we can write a
utility function isfull()
to
check whether the stack
is
full. We
cannot store elements more
than that size in the stack.
The isfull()
returns
TRUE,
if the
stack is full. The code of
the class definition is very
simple. We write T wherever
we need a
generic data type. It can be
written as under.
template
<class T>
class
Stack
{
Page
547
CS201
Introduction to Programming
private
:
int size
;
T array [
] ;
public
:
Stack ( )
;
void
push ( T ) ;
T pop ( )
;
bool
isEmpty ( ) ;
bool
isFull ( )
};
In the
definition of the functions of the
class, we again use
<T> immediately after
the
name of
the class. It will be
followed by the resolution operator
(::) and the
function
name
and finally we write T, wherever we
want to use generic data
type. It's a
definition
of the
class Stack. While using
it, say for int, we
write Stack
<int> and
provide a
initializer
value so that it can
determine the size of the
array. We read it as `create a
stack
of type
int'. Similarly Stack<double>x
will
mean x
is a
stack of type double. The
main
advantage
of this process is that we
write the Stack
class
once as the behavior is
common
regardless
of the type of the data we
want to put on. Stack class
can be used for int,
double
or
char
data
type.
This is
the analysis of the example of
Stack class, now as a programmer, it is
left to you
to write
the complete code of the
example.
Sample
Program
Here is a
sample program that demonstrates
the use of template
class.
/* This
program defines a template class
and shows its use for
different data types.
There is
also the use of template
function. It also overloads
the << operator.
*/
#include<iostream.h>
template<class
T>
class
Generic
{
private:
T
instance;
public:
Generic(T
i);
void
print(void);
};
//generic
constructor
template<class
T>
Generic<T>::Generic(T
i=0)
{
Page
548
CS201
Introduction to Programming
instance=i;
}
template<class
T>
void
Generic<T>::print(void)
{
cout<<"Generic
printing: "<<endl;
cout<<instance<<endl;
}
class
Employee
{
private:
int
idNum;
double
salary;
public:
Employee(int
id);
friend
ostream& operator
<<(ostream& out, const Employee
&e);
};
Employee::Employee(int
id=0)
{
idNum=id;
salary=4.9;
}
ostream&
operator<<(ostream &out, const Employee
&emp)
{
out<<"Employee
number "<<emp.idNum;
out<<"
Salary "<<emp.salary;
return(out);
}
void
main()
{
Generic<int>anInt(7);
Generic<double>someMoney(6.65);
Generic<Employee>
aWorker(333);
anInt.print();
someMoney.print();
aWorker.print();
}
Following
is the output of the
program.
Page
549
CS201
Introduction to Programming
Generic
printing:
7
Generic
printing:
6.65
Generic
printing:
Employee
number 333 Salary
4.9
Advantages
and Disadvantages of
Templates
Although,
most of the following uses
can also be implemented without
templates;
templates
do offer several clear
advantages not offered by any
other techniques:
·
Templates are easier to
write than writing several
versions of your similar
code
for
different types. You create
only one generic version of
your class or
function
instead
of manually creating
specializations.
·
Templates can be easier to
understand, since they can
provide a straightforward
way of
abstracting type information.
·
Templates are type-safe.
This is because the types
that templates act upon
are
known at
compile time, so the
compiler can perform type checking
before errors
occur.
·
Templates help in utilizing
compiler optimizations to
the
extreme.
Then of
course there is room for
misuse of the templates. On
one hand they provide
an
excellent mechanism to
create specific type-safe
classes from a generic
definition with
little
overhead. On the other hand,
if misused
·
Templates can make
code difficult to read and
follow depending upon
coding
style.
· They
can present seriously confusing syntactical problems
esp. when the code
is
large
and spread over several
header and source
files.
·
Then, there are times,
when templates can
"excellently" produce nearly
meaningless
compiler errors thus requiring extra
care to enforce syntactical
and
other
design constraints. A common mistake is
the angle bracket
problem.
Standard
Template Library
(STL)
Templates
are a major code reuse
feature. History of C++
language reveals that
the
template
feature was introduced later, relative to
other features. But it is a
very important
feature.
We will realize that it makes a
lot more sense to keep
total code base very
small
and
very concise. It also helps
ensure that the same
tested code is used
everywhere. We
had
earlier referred to this concept
while writing classes. We
separated the interface
and
implementation
and sealed the
implementation after testing
it. Afterwards, we
created
different
objects of the class and
every object knew its
behavior. Thus there was
an
abstraction
of details. The template functions
and template classes go
one-step even
further.
With templates, we can
perform different tasks
while using one base
code.
Objects
of different types staying with
one particular framework can be
instantiated. This
framework
(template) is so important that a couple
of researchers actually sat
down and
Page
550
CS201
Introduction to Programming
started
looking at that in programming we often
are using the one
concept which applies
to so
many things that we should templatise
it. For example, with the
help of arrays, we
do
different manipulations like, `next
element', go to the end of
the array, add
something
at the
end etc. Now suppose
that the size of array is
100. We want to add the
101st
element
in the array. We can do it by
copying the same array in a
new big array
and
adding
the element to that array.
Thus we have solutions for
different problems, but
these
are
the things of very common
use. Their every day
use is so important that
two
researchers
wrote a whole library of
common use functions. This
library is a part of
the
official
standard of C++. It is called STL i.e.
Standard Template Library. As a library,
it
is a
tested code base. Some
one has written, tested
and compiled for the
ultimate use of
programmers.
We can use these templates
and can implement different
concepts for our
own
data types. Equally is true
about the use of the
array data type. Our code
will become
very
small with the use of
this tested facility.
Similarly, there is no bug or
error in it.
Thus, if
we have a tested and tried
code base, we should try
our best to write programs
by
using
it. STL is a lot of
important code, pre-developed
for us. It is available as a
library.
We can
write programs by using it.
Thus our programs will be
small and error
free.
Page
551
Table of Contents:
|
|||||