Day No
Topics to be covered
Day#1 Basic concepts of OOPS, class and object
definition
Naming conventions
Inline functions
Scope resolution operator
Default arguments
Function overloading
Constructors and Destructors
Static members
Type conversions
Friends
C++
Horizon
Day No
Topics to be covered
Day#2
Operator Overloading
The string class
Inheritance
Access modifiers
Multiple inheritance
Containership
Day#3
Formatting and manipulators
I/O files and streams
Day#4 Template functions and classes
Exception Handling
C++
Horizon
Day No
Topics to be covered
Day#5
Virtual functions and dynamic polymorphism
Vtables
Day#6
Day#7
The debugger
Day#8
STL and Generic Programming
C++
Interfaces
RTTI
Namespaces
Memory Management
Horizon
Concepts of OOPS
C++
Horizon
A PIE
Abstraction :
The ability for a program to ignore some aspects of the information that it
is manipulating, i.e. the ability to focus on the essential.
It is the process of creating a category ( class ) by identifying those features
of the category which give a nearly complete picture of the class as is
required by the programmer.
Encapsulation :
It is the process of encapsulating all the identified data and functional
properties for the category as a single entity, called class.
It ensures that users of an object cannot change the internal state of the
object in unexpected ways; only the object's own internal methods are
allowed to access its state. Each object exposes an interface that specifies
how other objects may interact with it.
C++
Horizon
Polymorphism :
It gives the ability to take more than one forms. That is, an operation may
exhibit different behavior in different instances.
Object-oriented languages can make message sends; the specific method
which responds to a message send depends on what specific object the
message is sent to. This gains polymorphism, because a single variable in
the program text can hold different kinds of objects as the program runs,
and thus the same program text can invoke different methods at different
times in the same execution.
Inheritance :
It is the process by which an object of a particular category acquires the
properties of another category thus generating a hierarchical classification
of objects.
It organizes and facilitates polymorphism and encapsulation by permitting
objects to be defined and created that are specialized types of alreadyexisting objects - these can share (and extend) their behavior without
having to reimplement that behavior.
C++
Horizon
OBJECTS AND CLASSES
Objects :
An object is a software bundle of related variables and methods.
Real world objects share two characteristics: they all have state and
behavior.
Example :
Dogs have state (name, color, breed, hungry) and behavior (barking,
fetching, and wagging tail).
Bicycles have state (current gear, current pedal cadence, two wheels,
number of gears) and behavior (braking, accelerating, slowing down,
changing gears).
Software objects are modeled after real-world objects - they too have
state and behavior.
A software object maintains its state in one or more variables - an item
of data named by an identifier.
A software object implements its behavior with methods - a function
(subroutine) associated with an object.
C++
Horizon
The object's variables make up the center, or nucleus, of the object.
Methods surround and hide the object's nucleus from other objects in
the program.
Packaging an object's variables within the protective custody of its
methods is called encapsulation
This conceptual picture of an object a nucleus of variables packaged
within a protective membrane of methods is an ideal representation
of an object and is the ideal that designers of object-oriented systems
strive for.
Often an object may wish to expose some of its variables or hide some
of its methods. An object can specify one of four access levels for each
of its variables and methods. The access level determines which other
objects and classes can access that variable or method.
C++
Horizon
Encapsulating related variables and methods into a neat software bundle is a
simple yet powerful idea that provides two primary benefits :
Modularity: The source code for an object can be written and maintained
independently of the source code for other objects. Also, an object can be
easily passed around in the system.
You can give your bicycle to someone else, and it will still work.
Information hiding: An object has a public interface that other objects can
use to communicate with it. The object can maintain private information and
methods that can be changed at any time without affecting the other objects
that depend on it.
You don't need to understand the gear mechanism on your bike to use it.
C++
Horizon
Classes :
A class is a blueprint or prototype that defines the variables and the methods
common to all objects of a certain kind.
In the real world, you often have many objects of the same kind. For
example, your bicycle is just one of many bicycles in the world. Using
object-oriented terminology, we say that your bicycle object is an instance
of the class of objects known as bicycles. Bicycles have some state (current
gear, current cadence, two wheels) and behavior (change gears, brake) in
common. However, each bicycle's state is independent of and can be
different from that of other bicycles.
When building bicycles, manufacturers take advantage of the fact that
bicycles share characteristics, building many bicycles from the same
blueprint. It would be very inefficient to produce a new blueprint for every
individual bicycle manufactured.
In object-oriented software, it's also possible to have many objects of the
same kind that share characteristics: rectangles, employee records, video
clips, and so on. Like the bicycle manufacturers, you can take advantage of
the fact that objects of the same kind are similar and you can create a
blueprint for those objects.
A software blueprint for objects is called a class.
C++
Horizon
10
RULES OF THUMB
for class designing
If you can think of it as a separate idea, make it a class.
If you can think of it as a separate entity, make it an object
of some class.
If two classes have something significant in common, make
that commonality a base class.
C++
Horizon
11
MESSAGES
An object is a software bundle of related variables and methods.
Software objects interact and communicate with each other using
messages.
A single object alone is generally not very useful. Instead, an object usually
appears as a component of a larger program or application that contains
many other objects.
Your bicycle hanging from a hook in the garage is just a bunch of metal
and rubber; by itself, the bicycle is incapable of any activity. The bicycle is
useful only when another object (you) interacts with it (pedal).
Software objects interact and communicate with each other by sending
messages to each other. When object A wants object B to perform one of
B's methods, object A sends a message to object B.
C++
Horizon
12
Sometimes, the receiving object needs more information so that it knows
exactly what to do; for example, when you want to change gears on your
bicycle, you have to indicate which gear you want.
This information is passed along with the message as parameters.
A message has three parts :
The object to which the message is addressed
The name of the method to perform
Any parameters needed by the method
An object's behavior is expressed through its methods, so (aside from direct
variable access) message passing supports all possible interactions between
objects.
Messages provide important benefits - Objects don't need to be in the same
process or even on the same machine to send and receive messages back and
forth to each other.
C++
Horizon
13
OOAD
C++
Horizon
14
The starting point for object-oriented analysis is to identify
candidate objects and their relationships. In fact, this is the be-all
and end-all of object-oriented analysis.
The first stage can be a simple brain-storming of the possible
objects. One method is to go through all the nouns in any
documentation about the world you are analyzing, and
considering these as candidate objects.
Let us suppose that we are analyzing a system for a motor
museum. The system is to produce a multi-media guide to the
museum.
A preliminary list of objects might be:
car, bus, vehicle, number plate, exhibit, manufacturer, date of
manufacture, value, position, weight, size, photograph, tools,
shop, garage, ticket, owner, history
C++
Horizon
15
Removing synonyms and/or generalizing
The first stage is to try and group together items which are either
just different names for the same thing, or whether there is a
common generalization. The obvious thing above is to examine
the following group :
car, bus, vehicle, number plate, exhibit, photograph, tools, garage
It would seem that these could all be grouped together under the
object exhibit. In the museum the behavior of cars, photographs,
and garages are not radically different, so we can group them
together. We can differentiate between the individual objects on
the basis of an attribute, say type.
We are then left with the list of candidate objects:
exhibit, manufacturer, date of manufacture, value, position,
weight, size, shop, ticket, owner, history
C++
Horizon
16
Look for attributes
Attributes are themselves objects, but with trivial behavior essentially all you do is record their value. Considering the above
list, the following are probably attributes of exhibit:
date of manufacture, value, position, weight, size, owner
And we are left with the candidate objects:
exhibit, manufacturer, shop, ticket, history
We might have considered manufacturer as an attribute, but it
may be useful to separate out manufacturer information later in
the design. So we keep it for the time-being. The time to fold it in
as an attribute is when we find it has no discernible behavior of
its own.
C++
Horizon
17
Irrelevant objects
Sometimes an object is irrelevant.
Here the ticket object may be very relevant for a sales desk
system, but not for a multi-media system. Likewise, the shop is
irrelevant.
So we strike these out and we are left with the following objects.
exhibit, manufacturer, history
We have these objects potentially linked in the following way.
C++
Horizon
18
The process of development
The approach to development recommended here is an iterative one.
It involves repeated refinement of the object model. The process
needs to be controlled by an appropriate project management
process, involving reviews and check pointing.
C++
Horizon
19
Naming Convention
This is a set of rules for choosing the character sequence to be
used for identifiers which denote variables, types and functions
etc. in source code and documentation.
The reasons for using a naming convention (as opposed to
allowing programmers to choose any character sequence) include
the following :
to reduce the effort needed to read and understand source code
to enhance source code appearance (for example, by disallowing
overly long names or abbreviations)
C++
Horizon
20
Some of the potential benefits that can be obtained by adopting a naming
convention include the following :
to provide additional information about the use to which an identifier is put
to help promote consistency within a development team
to enhance clarity in cases of potential ambiguity;
to enhance the aesthetic and professional appearance of work product (for
example, by disallowing overly long names, comical or "cute" names, or
abbreviations);
to provide meaningful data to be used in project handovers which require
submission of program source code and all relevant documentation and
to provide better understanding in case of code reuse after a long interval of
time - well-chosen identifiers make it significantly easier for subsequent
generations of analysts and developers to understand what the system is doing
and how to fix or extend the source code for new business needs.
C++
Horizon
21
The two most common conventions are :
The CamelCase notation (originally known as medial capitals)
The Hungarian Notation (which encodes either the purpose - "Apps
Hungarian, or the type - "Systems Hungarian" of a variable in its name.)
C++
Horizon
22
The CamelCase :
This is the practice of writing compound words or phrases in which the elements
are joined without spaces, with each element's initial letter capitalized within the
compound and the first letter is either upper or lower case - as in "LaBelle",
BackColor or "iPod".
The name comes from the uppercase "bumps" in the middle of the compound
word, suggestive of the humps of a camel.
The practice is known by many other names. In computer programming, it is
called Pascal case if the first letter is capitalized, and camel case otherwise.
The first letter of a camel-case compound may or may not be capitalized.
Camel case is by no means universal in computing. In some programming
languages, notably Lisp and Forth, compound names are usually separated by
hyphens.
The use of medial caps for compound identifiers is recommended by the coding
style guidelines of many organizations or software projects.
For some languages (such as Mesa, Pascal, Modula, Java and Microsoft's .NET)
this practice is recommended by the language developers or by authoritative
manuals
and has therefore become part
of the language's "culture".
C++
Horizon
23
The Hungarian Notation (original developer, Charles Simonyi) :
Hungarian notation is an identifier naming convention in which the name of a
variable or function indicates its type or intended use.
There are two types of Hungarian notation :
Systems Hungarian notation
and
Apps Hungarian notation.
The two notations differ is in the purpose of the prefixes.
Hungarian notation was designed to be language-independent, and found its first
major use with the BCPL programming language.
In Hungarian notation, a variable name starts with a group of lower-case letters
which are mnemonics for the type or purpose of that variable, followed by
whatever name the programmer has chosen
The first character of the given name can be capitalised to separate it from the
type indicators (as in CamelCase). Otherwise the case of this character denotes
scope.
C++
Horizon
24
The Systems Hungarian Notation :
In Systems Hungarian notation, the prefix encodes the actual data type of the
variable. For example:
lAccountNum : variable is a long integer ("l")
arru8NumberList : variable is an array of unsigned 8-bit integers ("arru8")
szName : variable is a zero-terminated string ("sz")
bReadLine(bPort,&arru8NumberList) : function with a byte-value return
code.
The Apps Hungarian Notation :
Apps Hungarian notation does not encode the actual data type, but rather, it
gives a hint as to what the variable's purpose is, or what it represents.
rwPosition : variable represents a row ("rw")
usName : variable represents an unsafe string ("us"), which needs to be
"sanitized" before it is used
strName : Variable represents a string ("str") containing the name, but does
not specify how that string is implemented
It is possible for code using Apps Hungarian notation to sometimes contain
Systems Hungarian when describing variables that are defined solely in terms of
their type.
C++
Horizon
25
The Hungarian notation always uses initial lower-case letters as mnemonics.
It does not prescribe the mnemonics themselves. There are several widely used
conventions, but any set of letters can be used, as long as they are consistent
within a given body of code.
Some of the suggested prefixes are :
pX is a pointer to another type X; this contains very little semantic
information.
d is a prefix meaning difference between two values; for instance, dY might
represent a distance along the Y-axis of a graph, while a variable just called y
might be an absolute position. This is entirely semantic in nature.
sz is a null or zero-terminated string. In C, this contains some semantic
information because it's not clear whether a variable of type char* is a pointer
to a single character, an array of characters or a zero-terminated string.
w marks a variable that is a word. This contains essentially no semantic
information at all.
b marks a byte, which in contrast to w might have semantic information,
because in C the only byte-sized data type is the char, so these are sometimes
used to hold numeric values.
C++
Horizon
26
The Rules :
This list of rules is comprehensive, and thus covers both common and rare
situations.
Variables
All variable names are composed of four elements: scope, prefixes, base type,
and qualifiers.
Not all elements are present in all variable names; the only part that is always
present is the base type.
Base Types
The base type should not be confused with the types supported directly by the
programming language; most base types are application specific (and are
based on structures, classes, enumerated values, etc, that are created for that
application).
Tags should be short (typically two or three letters) and somewhat mnemonic.
Because of the brevity, the mnemonic value will be useful only as a reminder
to someone who knows the application, and has been told what the basic
types are; the name will not be sufficient to inform (by itself) a casual viewer
what is being referred to.
It is essential that all tags used in a given application be clearly documented.
This is extremely useful in helping a new programmer learn the code.
C++
Horizon
27
There are a few standard types that appear in many different applications :
f a flag (boolean, logical)
ch a character.
sz a zero-terminated string. This is the way strings are represented in C/C+
+.
fn a function. Since about the only thing you can do with a function is take
its address, this almost always has a "p" prefix.
There are some more types that appear in many applications :
b a byte (8 bits).
w an int (as defined by the C/C++ compiler -- it does not imply a specific
size).
sw a short word (16 bits).
lw a long word (32 bits).
llw a long long word (64-bits).
C++
Horizon
28
Prefixes
The standard prefixes are:
p a pointer. For example, a pch is a pointer to a character.
a an array. For example, an ach is an array of characters.
I an index into an array. For example, an ich is used to index an ach (with
a ch as the result, i.e. ch = ach[ich]).
c a count.
d a difference between two instances of a type.
gr a group, usually of variable-size objects.
e an element of an array.
u a union. This is a rarely used prefix; it is used for variables that can hold
one of several types.
w a wide value. This is used in conjunction with ch and sz for double-byte
characters
u an unsigned value. Typically used as a prefix to w, lw, and sw.
C++
Horizon
29
Qualifiers
The standard qualifiers are:
Min the first element in a set. This is very similar to First, but typically refers
to the actual first element of an array, not just the first to be dealt with.
Max the upper limit of elements in a set.
Most the current last element in a set.
First the first element (in a set) to be dealt with.
Last the last element (in a set) to be dealt with.
Scope
Most variables are used within the scope of a function. For those that have a
broader scope, one of the following scopes is placed at the beginning of the
variable name (before any prefixes) :
m_ a member of a class. This is used to label the variables (but not methods)
in a class. This is used so that member variables can be easily
distinguished from local variables in the class methods.
g_
a global. This is applied to variables that are available to numerous
functions in multiple files.
s_
a static. This is applied to variables that are available to multiple
functions all within the same file, but not available to functions in other
files (essentially a global variable, but private to one file).
C++
Horizon
30
Functions
The above rules used for variable names do not work as well for functions.
Whereas the type of a variable is always quite important, specifying how that
variable may be used, the important part of a function is typically what it does.
In addition, the context for functions is usually the entire program, so there is more
chance for conflict.
Some rules for functions are as follows :
All function names are distinguished from variable names by the use of some
standard punctuation; in C/C++ this is done by capitalizing the first letter of the
function name (all variable names begin with a lowercase type).
If the function returns a value, its name begins with the type of the value
returned; if no value is returned, the name must not begin with a valid type.
If the function does more than just determine a return value based on its
parameters, follow the return type (if any) with a few words describing what the
function does (a verb followed by an object is usually good). Each word should
be capitalized.
If the function is defined as static (it can be called only from functions within its
file), an underscore (_) should be placed at the beginning of the name.
C++
Horizon
31
C++
C++
Horizon
32
A Sample C++ Program
#include <iostream>
using namespace std ;
int main( )
{
cout << Good Morning<< endl ;
cout << Please enter a number\n ;
int i ;
cin>>i ;
cout << The number you have entered is << i << endl ;
}
C++
Horizon
33
Structure of C++ Program
The code is organized as follows :
The class declarations are placed in a header file.
The definitions of member functions go in another file.
The main program is placed in the third file which includes the
other two files as it will be using the classes created in those
files.
C++
Horizon
34
Generating the Executable
The following command compiles and links a program to
generate an executable :
c++ try.c
This command generates a file called [Link] which is an
executable file.
The following only compiles the program file to generate an
object file :
c++ -c try.c
This generates a file try.o which is the object file
corresponding to the program file try.c.
This object file can then be linked by the c++ command to get
the executable file [Link].
C++
Horizon
35
The default executable produced by c++ is [Link] as the
executable. But the executable name can be changed by using
the o flag with c++
c++ try.o -o [Link]
c++ try.c -o [Link]
This command produces [Link] as the executable file.
C++
Horizon
36
Data Types and Control
Structures
Data Types :
The data types are the same as in C.
There is a new kind of variable that is introduced in C++. It is the
reference variable.
Example :
int i = 10 ;
int & x = i ;
x = 20 ;
cout<<value of i is <<i<<endl ;
Output : value of i is 20
C++
Horizon
37
A reference variable :
is another name for an already created variable
must be initialized at the time of declaration
can be created for built in as well as user defined data types
References are compiled as pointers which are implicitly
dereferenced at each use involve hidden dereference
operations that are costly
Application of reference variables in argument passing :
void f(int &i)
{ i += 10 ;}
main( )
{
int k = 12 ;
f(k) ;
cout << k ;
}
C++
Horizon
38
Using the const qualifier :
If a function argument is declared as const then that argument can
not be modified within the function. Else a compiler error is
generated.
Example :
void func( const int &x, int y) {
x = 10 ; // generates an error
y += x ; }
constant pointer (must be initialized during declaration) :
i.e., following statement is illegal :
char * const ptr1 = HELLO ;
ptr1 = new char[10] ;
// the address in ptr1 can not be modified henceforth
pointer to a constant :
int const * ptr2 = &m ;//here int and const can be interchanged without causing any effect
// ptr2 can point to any variable of matching type, but the contents of what it
And following is legal :
points to can not be changed. i.e., following is illegal :
*ptr2 = 80 ;
C++
Horizon
ptr2 = &k ;
39
The following code depicts the difference between a 'constant
pointer' and 'pointer to a constant' :
main() {
int x = 10, y = 9 ;
int *const p1 = &x ;
int const *p2 = &x ;
*p1 = 77 ;
*p2 = 55 ;
p1 = &y ;
p2 = &y ;
}
C++
Horizon
40
A member function in a class can be declared as const as given
below. In this case such a member function can not modify the
object on which the function has been invoked.
Example :
class someclass {
int datamem ;
public :
void memfunc (int y) const {
datamem = y ; // generates an error
}
}
C++
Horizon
41
Control Structures :
These are the same as in C.
C++
Horizon
43
Operators in C++
All C operators are valid in C++. The additional new
operators of C++ are listed below :
<<
insertion operator
>>
extraction operator
::
scope resolution operator
::*
to declare pointer to class member
->*
to access class member using pointer to the object
and pointer to that member
.*
to access class member using object name and
pointer to that member
delete memory release operator
new memory allocation operator
C++
Horizon
44
Examples of ::* and .* and ->* operators
class temp {
public :
char *val ;
void print(int x) { cout<<val<<"::"<<x<<'\n' ; }
temp(char *v)
{val = v ;}
};
typedef void (temp :: *P)(int) ;
void main() {
temp
z1("z1"), z2("z2") ;
temp
*ptr = &z2 ;
P pf = &temp::print ;
[Link](1) ; (z1.*pf)(2) ;
[Link](3) ; (ptr->*pf)(4) ;
(z2.*pf)(5) ;
}
C++
Horizon
46
Associativity of Operators
Unary operators and the assignment operators are right
associative.
Thus
a = b = c means a = (b = c)
AND
* p++ means * ( p++ )
The remaining operators are left associative.
Thus
a + b + c means (a + b) + c
a + b * c means a + (b * c) [* has higher precedence than +]
a + b c means (a + b) - c
C++
Horizon
47
Inline Functions
An inline function is a function for which the function call is
replaced by the complete function code by the compiler.
(Similarity with the macros)
Requirement for inline functions :There is a cost involved in
function calls (jumping to the func, saving registers, pushing
arguments to stack and returning back) that becomes appreciable
for small functions. Inline functions overcome this overhead.
It is different and far more superior than macros in the following
respects :
Inline functions are handled by the compiler while macros are
handled by the pre processor.
Macros do not undergo the usual error checking. This is not
true for inline functions.
C++
Horizon
48
If expressions are passed to macros then they are as is
passed to the replaced code. In case of inline functions
expressions passed as input arguments are first evaluated and
then passed to the invoked function.
Example :
inline int sq(int a)
#define sq(a) a*a
{
return (a*a) ;
main( )
}
{
cout << sq(3+2) ;
main( )
}
{
cout << sq(3+2) ;
}
Output :
25
11
C++
Horizon
49
How to make a function inline ?
By using the inline prefix to the function definition.
The use of the inline keyword does not ensure that the
function would definitely become inline. It is only a request to
the compiler which may ignore the request in case of the
following situations :
the function is too long
for functions returning values if a loop, a switch or a goto
exists
for functions not returning values if a return statement exists
if functions contain static variables
if the function is recursive
But inline functions make the program take up more memory
because the statements of these functions are reproduced at
each point the function is called.
C++
Horizon
50
Class Definition
A class can be seen as an extension of a structure with the
following differences :
A class by default provides the features of data hiding while
a structure does not.
By default the members of a class are private while those of
a structure are public.
A class data type is used to create a specific category
encapsulating the data and the functional properties of the
category.
C++
Horizon
51
A sample declaration :
class item
{
int number ;
float cost ;
public :
void getdata(int a, float b)
{
//code for getdata
}
void dispdata( )
{
//code for dispdata
}
};
C++
Horizon
52
In C++ we can declare variables of structures as follows :
struct my_struct
{
//members of the structure
};
my_struct s1, s2 ;
Thus we do not need to use the keyword struct while
declaring variables of the structure data type.
C++
Horizon
53
Object Creation
What is an object ?
An object is an instance of a class just as a variable is an instance
of a built-in data type or a user-defined structure.
Object Creation :
Objects for an existing class my_class can be declared as follows :
my_class obj1 , obj2 ;
Objects are also created by the use of the keyword new
C++
Horizon
54
What happens when an object is created ?
Objects are created statically in C++ through the above piece of
code.
For every object created, a unique block of memory is allocated
for all the data members of the class.
The member functions are loaded only once for the whole
class.
All the objects of a class share the same set of functions.
C++
Horizon
55
member function#1
COMMON FOR ALL OBJECTS
OF THE SAME CLASS
member function#2
Object #1
member variable#1
memory created when
functions defined
Object #2
Object #3
member variable#1 member variable#1
member variable#2 member variable#2 member variable#2
member variable#3 member variable#3 member variable#3
memory created when
objects defined
C++
A UNIQUE SET PER OBJECT
Horizon
56
Member Access :
For the item class declared earlier :
main( )
{
item i1 , i2 ;
[Link]( 10 , 12.6 ) ;
[Link]( 13 , 7.8 ) ;
[Link]( ) ;
[Link]( ) ;
}
The members of the class are accessed by using the dot operator
on the class object just as in the case of a structure.
C++
Horizon
57
Private members :
The members declared in the private section of a class have the
following properties :
Their accessibility is limited within the class only.
They are hidden for the outside world.
Public members :
The members declared in the public section of a class have the
following properties :
They can be accessed from any part of the code.
Every class must contain at least one non private member.
C++
Horizon
58
Example :
For the item class declared earlier :
main( )
{
item
i1 ;
[Link]=10; //error:cant access private member outside class.
[Link](10,12.6); // public mems can be accessed outside class
[Link]( ) ;
}
C++
Horizon
59
Scope Resolution Operator
It can be used to access a global variable from within an inner
block even if the inner block tries to hide the global variable
Example :
OUTPUT :
int
m = 10 ;
in inner loop
main( )
m=30
{
::m=10
int m = 20 ;
in outer loop
{
m=20
int m = 30 ;
::m=10
cout<<in inner loop\nm=<<m<<\n ;
cout<<::m=<<::m<<\n ;
}
cout<<in outer loop\nm=<<m<<\n ;
cout<<::m=<<::m<<\n ;
}
C++
Horizon
60
It is used for providing outside the class definition for member
functions.
Example :
class item {
int x , y ;
public :
void getdata(int a , int b) ;
void dispdata( ) ;
};
void item :: getdata(int a , int b) {
// code for getdata
}
void item :: dispdata( ) {
// code for dispdata
}
C++
Horizon
61
Default Arguments
This mechanism allows invocation of a function without specifying
all the arguments.
This is possible if the function has been declared with default
arguments.
Example :
float amount ( float p, int time, float r = 0.15) ;
Such a function can be invoked in either of the following ways :
float val = amount(5000, 7) ;
float val = amount(3000, 3, 0.18) ;
In the first case the third argument takes the default value of 0.15
C++
Horizon
62
Only trailing arguments can be assigned default values.
A function can have any number of default values.
They provide the flexibility to have scope for addition of new
parameters to an existing function.
C++
Horizon
63
Method Overloading
It allows us to use the same name for different functions.
It is generally used for functions with similar functionalities.
It results in static polymorphism also known as early
binding.
The functions with the same name are identified on the basis of
their unique input argument list.
Example :
Let there exist the following function declarations :
int add(int a , int b , int c) ;
int add(int a , int b) ;
Let there be the following function calls :
add(3 , 8) ; // invokes the second version
add(1 , 2 , 3) ; // invokes the first version
C++
Horizon
64
The function selection involves the following steps :
The compiler first tries to find an exact match in which the
types of actual arguments are the same and use that function.
If an exact match is not found, the compiler uses the integral
promotions to the actual arguments, example :
char to int
float to double
When both the above fail, the compiler tries to use the built in
conversions to the actual arguments and then uses the function
whose match is unique. Example :
int to double
derived* to base*
Finally if nothing works, the compiler matches using userdefined conversions.
C++
Horizon
65
If the conversion is possible to have multiple matches, then the
compiler generates an error message.
Example : Declarations Function call
long sq(long n) sq(10) ;
double sq(double x)
This generates an error because int can be converted to both
long as well as double.
Declarations Function call
void f(int x, int y, int z = 80) f(2, 20)
void f(int x, int y) f(3, 33, 333)
The first call generates an error while the second call executes
the first function.
C++
Horizon
66
Overloading Member Functions
from base and derived classes
A member function named 'f' in a class A will hide all other members named 'f'
in the base classes of A, regardless of return types or arguments.
To overload, rather than hide, a function of a base class A in a derived class B,
you introduce the name of the function into the scope of B with a using
declaration.
This is shown in the following code :
class par {
public :
void f() {cout<<"def f in par\n" ;}
void f(int i) {cout<<"1 arg f in par\n" ;}
void g() {cout<<"def g in par\n" ;}
};
class ch : public par {
public :
using par::f ;
void f(int i, int j) {cout<<"2 arg f in ch\n" ;}
};
main() {
ch obj ;
obj.g() ;
obj.f() ; obj.f(9) ; obj.f(2, 3) ;
}
C++
Horizon
67
Constructors and Destructors
Constructors :
are special functions
have the same name as the class name
are invoked automatically every time an object of an
associated class is created.
can not be invoked explicitly
do not have a return type
should not be declared in the private section of a class, else
objects of that class can not be created
[there should be at least one public constructor]
C++
Horizon
68
can be overloaded
can have default arguments
can not be referenced by their addresses
a constructor that takes no parameters is called the default
constructor
if there is no constructor within the class then and only then the
compiler supplies with a default constructor
C++
Horizon
69
Copy Constructor :
It takes a reference of an object of the same class as itself as an
argument.
It creates a new object by initializing each member of the new object
with the value of the corresponding member of the object passed as
argument.
When no copy constructor is defined within the class, the compiler
supplies its own copy constructor.
Example :
class temp {
public : temp( ) { cout << "constr\n" ; }
temp (temp &var) { cout << "copy constr\n" ; }} ;
main( ) {
temp
t1 , t4 ;
temp
t2(t1) ; // copy constructor invoked
temp
t3 = t1 ; // copy constructor invoked
t4 = t1 ; } // copy constructor not invoked
C++
Horizon
70
The following code displays how a pass-by-value invokes the copy constructor :
class temp {
public :
temp() {cout << "constr\n" ;}
temp(temp &) {cout << "copy constr\n" ;}
~temp() {cout << "destr\n" ;} } ;
void f(temp t) { }
void g(temp &t) { }
main( ) {
temp
obj ;
cout<<invoking f ----\n ;
f(obj) ;
cout<<invoking g ----\n ;
g(obj) ;
}
C++
Horizon
71
Destructors :
are special functions like constructors
have the same name as the class name preceded by a tilde
can be invoked explicitly
are invoked implicitly by the compiler upon exit from the
program (or block or function); basically it is invoked when
the object is no longer accessible
can not return anything and has no return type
can not take any input arguments overloaded destructors are
not possible
should not be declared in the private section of a class
C++
Horizon
72
Destructors are not inherited.
The body of a destructor is executed before the member
objects.
Destructors for nonstatic member objects are executed before
the destructors for base classes.
Member functions may be called from within a destructor.
An object of a class with a constructor / destructor cannot be a
member of a union.
Destructors are invoked implicitly:
when an auto or temporary object goes out of scope
for constructed static objects at program termination
through use of delete for objects created by new.
C++
Horizon
73
Can be explicitly called as shown in the following code:
class X {
int x ; char ch ;
public :
X(){cout<<"constr\n" ;}
~X(){cout<<"destr\n" ;}
};
main() {
X obj ;
X *ptr = new X ;
cout<<"explicit destr invocation\n" ;
ptr->~X() ; //delete ptr ; instead of ptr->~X() will cause further error
memcpy(ptr, &obj, sizeof(X)) ;
delete ptr ;
}
C++
Horizon
74
New and Delete Operators
These are used for dynamic allocation (new) and de-allocation
(delete) of variables and objects.
An object created by new is destroyed only when it is deleted.
Example :
main( )
{
class temp {
temp *ptr ;
CONS
public :
ptr = new temp ;
temp( ) {
}
cout << CONS\n ;
main( )
}
{
CONS
temp *ptr ;
~temp( ) {
DESTR
ptr = new temp ;
cout<<DESTR\n ;
delete(ptr) ;
}
}
};
C++
Horizon
75
Other Examples :
The following creates an integer variable with initial value 12 ;
int
* p1 = new int (12) ;
The following creates a memory space for an array of 10 integers :
int
*p2 = new int [10] ;
Code :
class temp {
public :
temp( ) {
cout<<CONS\n ; }
~ temp( ) {
cout<<DESTR\n; }
};
main( ) {
temp *p1 = new temp[5] ;
delete[ ] p1 ; }
C++
Horizon
Output :
CONS
CONS
CONS
CONS
CONS
DESTR
DESTR
DESTR
DESTR
DESTR
76
Following are the advantages of new over malloc :
It automatically computes the size of the data object, the use of
the sizeof operator is not required.
It automatically returns the correct pointer type, no type casting
is required.
It is possible to initialize the object while creating the memory
space.
As the other operators, new and delete can also be overloaded.
NEW RETURNS NULL POINTER IN CASE OF FAILURE.
C++
Horizon
77
Stack and Heap
The memory that a program uses is typically divided into four different areas :
The code area, where the program sits in the memory
The data segment, where the global variables are stored
The heap, from where the dynamically allocated variables are allocated
The stack, from where the parameters and the local variables are allocated
The Heap
The heap (also known as the free store) is a large pool of memory used for
dynamic allocation.
In C++, when the new operator is used to allocate memory, this memory is
assigned from the heap.
Because the precise location of the memory allocated is not known in advance,
the memory allocated has to be accessed indirectly which is why new
returns a pointer.
Sequential memory requests may not result in sequential memory addresses
being allocated.
When a dynamically allocated variable is deleted, the memory is returned to
the heap and can then be reassigned as future allocation requests are received.
C++
Horizon
78
The heap has advantages and disadvantages:
1) Allocated memory stays allocated until it is specifically deallocated (beware
memory leaks).
2) Dynamically allocated memory must be accessed through a pointer.
3) Because the heap is a big pool of memory, large arrays, structures, or classes
should be allocated here.
The Stack
A stack is a container that holds other variables (much like an array).
However, whereas an array allows access and modification of elements in
any order, a stack is more limited.
The operations that can be performed on a stack are identical to the ones
above:
1) Look at the top item on the stack (usually done via a function called
top())
2) Take the top item off of the stack (done via a function called pop())
3) Put a new item on top of the stack (done via a function called push())
A stack is a last-in, first-out (LIFO) structure - the last item pushed onto the
stack will be the first item popped off.
C++
Horizon
79
The stack in action
The sequence of steps that takes place when a function is called is as follows :
The address of the instruction beyond the function call is pushed onto the
stack. This is how the CPU remembers where to go after the function returns.
Room is made on the stack for the functions return type. This is just a
placeholder for now.
The CPU jumps to the functions code.
The current top of the stack is held in a special pointer called the stack frame.
Everything added to the stack after this point is considered local to the
function.
All function arguments are placed on the stack.
The instructions inside of the function begin executing.
Local variables are pushed onto the stack as they are defined.
C++
Horizon
80
When the function terminates, the following steps happen:
The functions return value is copied into the placeholder that was put on the
stack for this purpose.
Everything after the stack frame pointer is popped off. This destroys all local
variables and arguments.
The return value is popped off the stack and is assigned as the value of the
function. If the value of the function isnt assigned to anything, no assignment
takes place, and the value is lost.
The address of the next instruction to execute is popped off the stack, and the
CPU resumes execution at that instruction.
Stack overflow
Stack has a limited size, and can only hold a limited amount of information.
If the program tries to put too much information on the stack, stack overflow will
result.
Stack overflow happens when all the memory in the stack has been allocated
in that case, further allocations begin overflowing into other sections of memory.
C++
Horizon
81
Stack overflow is generally the result of allocating too many variables on the
stack, and/or making too many nested function calls (where function A calls
function B calls function C calls function D etc)
Overflowing the stack generally causes the program to crash.
The stack has advantages and disadvantages:
Memory allocated on the stack stays in scope as long as it is on the stack. It is
destroyed when it is popped off the stack.
Because the stack is relatively small, it is generally not a good idea to do
anything that eats up lots of stack space. This includes allocating large arrays,
structures, and classes, as well as heavy recursion.
C++
Horizon
82
Usage of typedef
A typedef declaration introduces a name that, within its scope, becomes a
synonym for the type given by the type-declaration portion of the declaration.
This is done as follows :
typedef type-declaration synonym;
A typedef declaration can be used to construct shorter or more meaningful
name for a type already defined by the language or for a user-defined type.
In contrast to the class, struct, union, and enum declarations, typedef
declarations do not introduce new types - they introduce new names for
existing types.
The typedef specifier can not be used inside a function definition.
Typedef names share the name space with ordinary identifiers. Therefore, a
program can have a typedef name and a local-scope identifier by the same
name.
Any type can be declared with typedef, including pointer, function, and array
types.
A typedef name can be declared for a pointer to a structure or union type before
the structure or union type is defined, as long as the definition has the same
visibility as the declaration.
Examples
One use of typedef declarations is to make declarations more uniform and
compact. For example:
typedef char CHAR;
// Character type.
typedef CHAR * PSTR;
// Pointer to a string (char *).
PSTR strchr( PSTR source, CHAR target );
typedef unsigned long ulong;
ulong ul; // Equivalent to "unsigned long ul;"
To use typedef to specify fundamental and derived types in the same
declaration, declarators can be separated with commas. For example:
typedef char CHAR, *PSTR;
The following example provides the type DRAWF for a function returning no
value and taking two int arguments:
void DRAWF( int, int );
After the above typedef statement, the declaration
DRAWF box;
would be equivalent to the declaration
void box( int, int );
typedef is often combined with struct to declare and name user-defined types:
typedef struct mystructtag {
int i; double f;
} mystruct;
int main() {
mystruct ms;
ms.i = 10; ms.f = 0.99;
printf("%d %f\n", ms.i, ms.f);
}
Recursion
Recursion is when a function calls itself.
Thus in the course of the function definition there is a call to that very same
function.
A check is made to see if a certain condition is true and in that case an exit
(return from) from the recursive function is made.
The case in which the recursion ends is called a base case.
Additionally, just as in a loop, some value must change and we must
incremently advance closer to our base case.
Example :
void myFunction( int counter) {
if(counter == 0)
return;
else
{
cout <<counter<<endl;
myFunction(--counter);
return;
}
}
C++
Horizon
86
Every recursion should have the following characteristics :
A simple base case which we have a solution for and a return value.
Sometimes there are more than one base cases.
A way of getting our problem closer to the base case. I.e. a way to chop out
part of the problem to get a somewhat simpler problem.
A recursive call which passes the simpler problem back into the function.
The key to thinking recursively is to see the solution to the problem as a
smaller version of the same problem.
C++
Horizon
87
Command Line Arguments
arguments received by the program that are given at the
command line while starting a program
in the following definition of main function :
main( int argc, char ** argv)
the command line arguments are received inside argc and argv
as follows :
argc : holds the number of parameters including the program
name
argv : is an array of strings
each string holds one argument
the first string pointed by argv[0] is the program name
the remaining strings held in argv[1] to argv[argc 1]
are the input arguments to the program
C Data Structures
Horizon
88
Static Members
STATIC DATA MEMBERS :
Static member variables have the following properties :
It is initialized to 0 by default when it is created.
Only one copy of a static data member is created per class.
A static data member is shared by all the objects of the that
class.
A static member variable must be defined outside the class.
Example :
THIS DECLARATION CREATES
class temp {
THE STATIC VARIABLE
static int x ;
};
int temp :: x ;
C++
Horizon
89
It can be accessed with the class name also.
Example :
temp::x = 19 ;
Static variables are also known as class variables and are used
to hold class wide information.
They can be created with some initial value as shown below :
class temp {
static int x ;
};
int temp :: x = 17 ;
C++
Horizon
90
STATIC MEMBER FUNCTIONS :
Static member functions have the following properties :
A static member function can access only the static members
of the class.
A static member function does not have a this pointer. So it
can access nonstatic members of its class only by using . or
-> operators.
A static member function can not be virtual.
There can not be a static and a nonstatic member function with
the same name and the same arguments.
C++
Horizon
91
They can be invoked by using the class name. They can also
be invoked on an object of the class but still there behavior
remains the same.
Example :
class temp {
public :
static void f( ) {
cout<<in static func ;
}
};
main( ){
temp :: f( ) ;
(new temp)->f( ) ;
temp obj ; obj.f( ) ;
}
C++
Horizon
92
For a local variable declared static in a member function :
a static variable is available to the whole program
thus, all instances of the type share the same copy of the
static variable.
Note : Assigning to a static local variable is not thread safe and
is not recommended as a programming practice.
Example :
class C {
public : void Test(int value) {
static int var = 0;
if (var == value)
cout << "var == value" << endl;
else
cout << "var != value" << endl;
var = value; }};
int main() {
C c1; C c2;
[Link](100); [Link](100); }
C++
Horizon
93
Exercises
Create a class stack such that the stack is either for characters
or for integers, the stack type being an input. Maximum
number of items that the stack can hold should be defined by
the user at the time of object creation. Test it by creating two
objects one for integers and one for characters.
Create a class complex which provides the following
functions : add, subtract and multiply. Write only one add
function through which you can add 2 or 3 or 4 complex
numbers. These functions should return the complex number
holding the result.
C++
Horizon
94
Create a class hospital with the following data members :
name, staffStrength, noOfPatients, areaCode. Let there be
the following member functions of the class :
patientAdmitted, patientReleased, staffResigns, staffJoins,
displayInfo. Let there be a function which shows the total
number of hospitals on the basis of the area code, assume
that the area is divided into 3 area codes. Test the class by
creating sufficient number of objects.
C++
Horizon
95
Friend Functions and Classes
Friend functions are those functions which have access to
the private and protected members of a class although they
are themselves not members of the class.
Example :
class temp {
int
i;
public :
temp( ) {i = 10 ;}
friend void disp( temp t) ;
};
void disp( temp t) {
cout <<t.i ;
}
C++
Horizon
96
Following are the special features of a friend function :
It is not in the scope of the class to which it has been declared
as a friend.
It cannot access member names directly and can access the
members only through an object of the class. That means the
friend function accesses the members of a class for an object of
that class.
It can be declared as a friend either in the public or private
section of the class, its meaning remaining the same.
Friendship is not a symmetric relationship. It is neither
inherited nor transitive.
When a friend declaration refers to an overloaded name or
operator, only the function specified by the argument types
becomes the friend.
C++
Horizon
97
Some Examples :
Following code shows how an outside function is friendly to a class :
class temp {
int
i;
public :
temp( ) {i = 10 ;}
friend void disp( temp t) ;
};
void disp( temp t) {
cout <<t.i ;
}
main( ) {
temp t ;
disp(t) ;
}
C++
Horizon
98
Following code shows how a function of one class be friendly to a
function of another class :
class X {
public :
int f( ) { /* code */ }
};
class Y {
friend int X::f( ) ;
};
If a class (X) declares another class (Y) as a friend class then all
the members of the friend class (Y) can access all the members of
the class X.
Following code shows how a class can be declared as friendly :
class X {
friend class Y ;
};
C++
Horizon
99
Following code shows friendship is neither transitive nor inherited:
class A {
int a ;
friend class B ;
};
class B {
friend class C ;
};
class C {
// NON TRANSITIVE
public : void f(A *p) {
p -> a++ ; }
//error
};
class D : public B {
// NON INHERITABLE
public : void f(A *p) {
p -> a++ ; }
//error
};
C++
Horizon
100
Following code shows friend declaration referring to an
overloaded name :
class test {
int i ;
friend void f(test k) ;
friend void f(test k, char c) ;
public :
test(){i = 10 ;}
};
void f(test i, char c) {cout << "in 1st f :: "<<i.i<<"\n" ;}
void f(test i) {cout << "in 2nd f :: "<<i.i<<"\n" ;}
void main() {
testobj ;
f(obj) ;
}
C++
Horizon
101
Declaring a class to be a friend also implies that private and
protected names from the class granting friendship can be used in
the class receiving it. Example :
class X {
struct sx {} ;
void f() {cout<<"in class X\n" ;}
friend class Y ; } ;
class Y {
public :
void f_Y() {
X::sx sx_obj ;
X x_obj ;
x_obj.f() ;} } ;
void main() {
Y y_obj ;
y_obj.f_Y() ;
}
C++
Horizon
102
Operator Overloading
This feature provides with the flexibility of creating new definition
for most of the C++ operators.
Following operators can not be overloaded :
class member access operators ( . and .* )
scope resolution operator ( :: )
size operator ( sizeof )
conditional operator ( ?: )
C++
Horizon
103
Only the semantics of an operator can be modified, not its syntax.
That is, the rules like the number of operands and precedence
rules can not be modified.
Operator overloading is achieved through the use of operator
functions.
Operator functions are either :
non-static member functions or
friend functions
C++
Horizon
104
OVERLOADING UNARY OPERATORS :
Using Member Functions :
class vector {
int x1, x2, x3 ;
public :
vector( ) { x1 = 10 ; x2 = 20 ; x3 = 30 ; }
void operator ( ) {
x1 = -x2 ; x2 = -x3 ; x3 = -x1 ;
}
void disp( ) {
cout << x1 ::<<x1<< x2::<<x2<< x3::<<x3 ;
}
};
main( ) {
vector v1 ;
-v1 ;
[Link]( ) ;
} C++
Horizon
105
Using Friend Function :
class vector {
int x1, x2, x3 ;
public :
vector( ) { x1 = 10 ; x2 = 20 ; x3 = 30 ; }
friend void operator (vector &v ) ;
void disp( ) {
cout << x1 ::<<x1<< x2::<<x2<< x3::<<x3 ;
}
};
void operator (vector &v) {
v.x1 = -v.x2 ; v.x2 = -v.x3 ; v.x3 = -v.x1 ;
}
main( ) {
vector v1 ;
-v1 ;
[Link]( ) ;
}
C++
Horizon
106
OVERLOADING BINARY OPERATORS :
Using Member Functions :
left hand operand :class vector {
invokes the operator func
int x1, x2, x3 ;
right hand operand :public :
passed as an argument
vector( int a, int b, int c) { x1 = a ; x2 = b ; x3 = c ; }
vector operator +( vector v) {
return vector (2*x1 + v.x1, 2*x2 + v.x2, 2*x3 + v.x3) ;
}
void disp( ) {
cout << x1 ::<<x1<< x2::<<x2<< x3::<<x3 ;
}
};
main( ) {
vector v1(10, 20, 30) , v(1, 2, 3) ;
vector v2 = v1+v ;
[Link]( ) ;
}
C++
Horizon
107
Using Friend Function :
class vector {
int x1, x2, x3 ;
public :
vector( int a, int b, int c) { x1 = a ; x2 = b ; x3 = c ; }
friend vector operator +( vector v, vector u) ;
void disp( ) {
cout << x1 ::<<x1<< x2::<<x2<< x3::<<x3 ;
}
};
vector operator +( vector v, vector u) {
return vector (u.x1-v.x1, u.x2-v.x2, u.x3-v.x3) ;
}
main( ) {
vector v1(10, 20, 30) , v(1, 2, 3) ;
vector v2 = v1+v ;
[Link]( ) ;
}
C++
Horizon
108
Rules for overloading operators :
Only existing operators can be overloaded.
The overloaded operator must have at least one user defined
type operand.
The syntax rules of the original operators can not be changed.
When using binary operators overloaded through a member
function, the left hand operand must be an object of the
relevant class.
Binary arithmetic operators like +, -, *, / must explicitly
return a value.
C++
Horizon
109
OPERATORS THAT CANNOT BE OVERLOADED USING
FRIENDS
=
()
[]
->
Assignment operator
Function call operator
Subscripting operator
Class member access operator
This is to ensure that their first operands are lvalues.
{Friend functions can take variables, i.e., non-objects as first
argument also. This should not happen for these overloaded
operators.}
[
An lvalue is an expression referring to an object or function.
Originally the term was used to mean something that can be on
the left hand side of an assignment
]
C++
Horizon
110
WHY THE NEED FOR THE FRIEND FUNCTIONS FOR
OPERATOR OVERLOADING
Situations arise where a member function usage for operator
overloading does not work.
Eg :
A = 2 + B // both A and B are objects.
This can not be achieved through a member function because
the left hand operand is responsible for invoking the member
function.
In this case the left hand operand is not an object.
C++
Horizon
111
<< and >> operators :
These can be overloaded using only friend functions.
- The left hand operands for these operators are cout and
cin.
- These operands are system defined objects of the system
defined classes istream and ostream.
- So a user can only create a friend function to overload
these operators.
C++
Horizon
112
class complex { private : int real, img ;
public : complex() {}
complex(int r, int i) {
real = r; img = i ; }
friend ostream & operator << (ostream & s, complex & c) ;
friend istream & operator >> (istream & s, complex & c) ; } ;
ostream & operator << (ostream & s, complex & c) {
s<<"("<<[Link]<<","<<[Link]<<")"<<endl ;
return s ; }
istream & operator >> (istream & s, complex & c) {
s>>[Link]>>[Link] ;
return s ; }
void main( ) {
complex c1, c2(1, 2) ;
cout<<endl<<"c2 = "<<c2 ;
cout<<"enter a complex number\n" ;
cin>>c1 ;
cout << "c1 = "<<c1 ;
}
C++
Horizon
113
The operator<< function returns a reference to the ostream it was
called for, so that another operator<< can be applied to it.
Example :
cout<<x = <<x ;
This statement is interpreted as :
([Link]<<(x = )).operator<<(x)
Thus when several items are printed by a single output statement,
they will be printed from left to right.
C++
Horizon
114
OVERLOADING THE SUBSCRIPT OPERATOR
Subscripting is considered a binary operator.
The expression x [ y ] is interpreted as [Link][ ] (y) where
x is a class object.
class vector {
int arr[10] ;
public : vector( )
{
for (int i = 0; i < 10; i++)
arr[i] = i+1 ; }
friend int operator +(vector v, int x) ;
int operator [ ](int x){ int temp = 1 ;
for (int i = 0; i < x; i++)
temp = temp * arr[i] ;
return temp ;}} ;
int operator +(vector v, int x){
int temp = 1 ;
for (int i = 0; i < x; i++)
temp = temp * [Link][i] ;
return temp ;}
main( ) {
vector v1 ;
cout << "\nsubscript result is : " << v1[4] ;
cout << "\naddition result is : " << v1+3 ;}
C++
Horizon
115
THE CLASS MEMBER ACCESS
Class member access using -> is considered a unary operator.
The expression x -> y is interpreted as ([Link]->())->y
where x is a class object.
C++
Horizon
116
THE INCREMENT AND DECREMENT OPERATORS
The ++ and the -- operators can be overloaded as follows :
In the prefix form :
through a friend function:
- takes one argument0
through member function
- takes no argument
In the postfix form :
through a friend function:
- takes two arguments the second argument automatically
passed as 0
through member function
- takes one argument
C++
Horizon
117
overloaded ++, -- using member functions
class vector {
int x1, x2, x3 ;
public :
vector( ) { x1 = 10 ; x2 = 20 ; x3 = 30 ; }
//postfix form
void operator ++(int i) {
for(int cntr = 0; cntr < 2; cntr++) {
x1++ ; x2++ ; x3++ ; } }
//prefix form
void operator ++( ) { x1++ ; x2++ ; x3++ ; }
void disp( ) {
cout << "\n\nx1 ::"<<x1<<" x2::"<<x2<<" x3::"<<x3 ;}
};
main( ) {
vector v1 ; [Link]() ;
v1++ ; [Link]() ;
++v1 ; [Link]() ;
}
C++
Horizon
118
overloaded ++, -- using friend functions
class vector {
int x1, x2, x3 ;
public :
vector( ) { x1 = 10 ; x2 = 20 ; x3 = 30 ; }
friend void operator ++(vector &v, int j) ;
friend void operator ++(vector &v) ;
void disp( ) { cout << "\n\nx1 ::"<<x1<<" x2::"<<x2<<" x3::"<<x3 ;
};
void operator ++(vector &v, int j) {
for(int cntr = 0; cntr < 2; cntr++) { v.x1++ ; v.x2++ ; v.x3++ ; }
}
void operator ++(vector &v) {
v.x1++ ; v.x2++ ; v.x3++ ; }
main( ) {
vector v1 ; [Link]() ;
v1++ ; [Link]() ;
++v1 ; [Link]() ;
}
C++
Horizon
119
operator ( )
class A {
public :
void operator () () {
cout<<"func called\n" ;
}
void operator() (int x, char c) {
cout<<"another f : "<<x<<" "<<c<<endl ;
}
void f() {cout<<"in f\n" ;}
};
void main() {
A obj ;
obj() ;
obj(10, 'k') ;
}
C++
Horizon
120
Exercises
Create a class RationalNumber (fractions) with the following capabilities:
_ Create a constructor that prevents a 0 denominator in a fraction, reduces or
simplifies fractions that are not in reduced form and avoids negative
denominators.
_ Overload the addition, subtraction, multiplication and division operators for
this class.
_ Overload the relational and equality operators for this class.
Develop a class Polynomial. The internal representation of a Polynomial is an
array of terms. Each term contains a coefficient and an exponent. For example,
the term 2x4 has the coefficient 2 and the exponent 4. Develop a complete class
containing proper constructor and destructor functions as well as set and get
functions. The class should also provide the following overloaded operator
capabilities:
_ Overload the addition operator (+) to add two Polynomials.
_ Overload the subtraction operator (-) to subtract two Polynomials.
_ Overload the assignment operator to assign one Polynomial to another.
_ Overload the multiplication operator (*) to multiply two Polynomials.
_ Overload the addition assignment operator (+=), subtraction assignment
operator (-=), and multiplication assignment operator (*=).
C++
Horizon
121
Shallow vs Deep copying
A shallow copy of an object copies all of the member field values.
This works well if the fields are values, but may not be what is wanted for
fields that point to dynamically allocated memory
the pointer will be copied. but the memory it points to will not be
copied
the field in both the original object and the copy will then point to the
same dynamically allocated memory, which is not usually what is
wanted.
The default copy constructor and assignment operator make shallow copies.
A deep copy copies all fields, and makes copies of dynamically allocated
memory pointed to by the fields.
To make a deep copy, a copy constructor must be written and the assignment
operator overloaded, otherwise the copy will point to the original, with
disastrous consequences.
C++
Horizon
122
Thus :
If an object has pointers to dynamically allocated memory,
and
the dynamically allocated memory must be copied when the original object is
copied,
then a deep copy is required.
A class that requires deep copies generally needs :
A constructor to either make an initial allocation or set the pointer to NULL.
A destructor to delete the dynamically allocated memory.
A copy constructor to make a copy of the dynamically allocated memory.
An overloaded assignment operator to make a copy of the dynamically
allocated memory.
C++
Horizon
123
Type Conversions
Following three types of conversions will be discussed :
from built in type to class type
from class type to built in type
from one class type to another class type
C++
Horizon
124
Built In Type to Class Type :
The following statements are true under this situation :
It is achieved through a constructor function.
This constructor belongs to the destination class.
Example :
class integer {
int
val ;
public :
integer (int i) { val = i ;}
integer(char *s) {val = strlen(s) ;}
};
main( ) {
integer object(10) ;
integer obj = 90 ;
obj = 88 ;
obj = abcd ;
}
C++
Horizon
125
Class Type to Built In Type :
The following statements are true under this situation :
It is achieved through a casting operator function.
This casting operator function belongs to the source class.
User-defined conversion cannot specify a return type
Example :
class vector {
double x, y, z ;
public :
vector() {
x=y=z=9;}
operator double() {
return (x+y+z) ; }
};
main( ) {
vector v ;
double l = v ; }
C++
// NOTE :: no return type specified.
Horizon
126
Class Type to Another Class Type :
The following statements are true under this situation :
It can be achieved through :
a casting operator function, in this case the function
belongs to the source class
a constructor function, in this case the constructor
belongs to the destination class
C++
Horizon
127
Example : c1 is source class and c2 is the destination class.
The following code shows the usage of the casting operator function in
the source class.
class c2 ;
class c1 {
public :
int i, j, k ;
c1(int a, int b, int c) {
i = a; j = b ;k = c ; }
operator c2( ) {
c2 temp ;
temp.x = i+j ; temp.y = k ; return temp ; }
};
The following code shows the usage of the constructor function in the
destination class.
class c2 {
public :
int x, y ;
c2(c1 p) {
x = p.i ; y = p.k ; }
};
C++
Horizon
128
class c2 ;
class c1 {
public :
int i, j, k ;
c1(int a, int b, int c) {
i = a; j = b ;k = c ; }
operator c2( ) ;
};
casting operator func in the
source class
with forward declaration
class c2 {
public :
int x, y ;
c2() {x = 0; y = 0 ;}
};
c1 :: operator c2() {
c2 temp ;
temp.x = i+j ; temp.y = k ; return temp ; }
void main() {
c1 ob1(1, 2, 3) ;
c2 ob2(ob1) ;
}
C++
Horizon
129
class c1 ;
class c2 {
public :
int x, y ;
c2(c1 p) ;
};
constructor func in the
destination class
with forward declaration
class c1 {
public :
int i, j, k ;
c1(int a, int b, int c) {
i = a; j = b ;k = c ; }
};
c2 :: c2 (c1 p) {
x = p.i ; y = p.k ; }
void main() {
c1 ob1(1, 2, 3) ;
c2 ob2(ob1) ;
}
C++
Horizon
130
The std::string class
String objects are a special type of container, specifically designed to operate
with sequences of characters.
Unlike traditional c-strings, which are mere sequences of characters in a
memory array, C++ string objects belong to a class with many built-in features
to operate with strings in a more intuitive way and with some additional useful
features common to C++ containers.
The string class is an instantiation of the basic_string class template, defined in
<string> as:
typedef basic_string<char> string;
The following example shows a simple usage of the class :
#include <iostream.h>
#include <string>
using namespace std ;
void main() {
string
obj("hello") ;
cout<<[Link]()+2 ; }
C++
Horizon
131
In the C++ programming language, the std::string class is a standard
representation for a string of text.
This class removes many of the problems introduced by C-style strings by
putting the onus of memory ownership on the string class rather than on the
programmer.
The class provides some typical string operations like comparison,
concatenation, find and replace, and a function for obtaining substrings.
It can be constructed from a C-style string, and a C-style string can also be
obtained from it.
The main constructors provided by the class are :
string ( );
string ( const string& str );
string ( const string& str, size_t pos, size_t n = npos );
string ( const char * s, size_t n );
string ( const char * s );
string ( size_t n, char c );
template<class InputIterator> string (InputIterator begin, InputIterator end);
C++
Horizon
132
Some important member functions :
length
capacity
find
insert
append
c_str
data
compare
substr
The overloaded operators are :
operator+=
operator=
operator[]
C++
Horizon
133
#include <iostream.h>
#include <string>
using namespace std ;
int main () {
char * cstr, *p;
string str ("Please split this phrase into tokens");
cstr = new char [[Link]()+1];
strcpy (cstr, str.c_str()); // cstr now contains a c-string copy of str
p=strtok (cstr," ");
while (p!=NULL) {
cout << p << endl;
p=strtok(cstr," ");
}
delete[] cstr;
return 0;
}
C++
Horizon
134
#include <iostream>
#include <string>
using namespace std;
int main () {
string str("to be question"); string str2("the "); string str3("or not to be") ;
string::iterator it;
[Link](6,str2); cout << str << endl;
// to be (the )question
[Link](6,str3,3,4); cout << str << endl;
// to be (not )the question
[Link](10,"that is cool",8); cout << str << endl; // to be not (that is )the question
[Link](10,"to be "); cout << str << endl; // to be not (to be )that is the question
[Link](15,1,':'); cout << str << endl; // to be not to be(:) that is the question
it = [Link]([Link]()+5,','); // to be(,) not to be: that is the question
[Link] ([Link](),3,'.'); cout << str << endl; // to be, not to be: that is the question(...)
cout << str << endl;
[Link] (it+2,[Link](),[Link]()+3); cout << str << endl;
return 0; }
C++
Horizon
// (or )
135
#include <iostream>
#include <string>
using namespace std;
int main ()
{
string name ("John");
string family ("Smith");
name += " K. ";
// c-string
name += family;
// string
name += '\n';
// character
cout << name;
return 0;
}
C++
Horizon
136
Exercises
Define a class vector to hold 10 integers. Overload the operators
+, -, =, *. Overload the subscript operator [] to retrieve a specific
integer from the vector.
Define a class String which behaves as the char * string. Overload
the following operators : + (to implement the string
concatenation), *, ++ and the -- operators. Provide a constructor
that creates a string of the specified size, in the absence of the
specified size, some default size of 50 chars is taken.
C++
Horizon
137
Inheritance
A class inherits state and behavior from its superclass.
Inheritance provides a powerful and natural mechanism for
organizing and structuring software programs.
Example : mountain bikes, road bikes, and tandems are all
kinds of bicycles. In object-oriented terminology, mountain
bikes, road bikes, and tandems are all subclasses of the bicycle
class. Similarly, the bicycle class is the superclass of mountain
bikes, road bikes, and tandems.
Each subclass inherits state (in the form of variable
declarations) from the superclass. Mountain bikes, road bikes,
and tandems share some states: eg speed. Also, each subclass
inherits methods from the superclass. Mountain bikes, road
bikes, and tandems share some behaviors: braking and
changing pedaling speed, for example.
C++
Horizon
138
However, subclasses are not limited to the state and behaviors
provided to them by their superclass. Subclasses can add
variables and methods to the ones they inherit from the
superclass. Tandem bicycles have two seats and two sets of
handle bars; some mountain bikes have an additional chain
ring, giving them a lower gear ratio.
Subclasses can also override inherited methods and provide
specialized implementations for those methods. For example, if
you had a mountain bike with an additional chain ring, you
would override the "change gears" method so that the rider
could shift into those lower gears.
You are not limited to just one layer of inheritance. The
inheritance tree, or class hierarchy, can be as deep as needed.
Methods and variables are inherited down through the levels. In
general, the farther down in the hierarchy a class appears, the
more specialized its behavior.
C++
Horizon
139
Class hierarchies should reflect what the classes are, not how
they're implemented. If we implemented a tricycle class, it
might be convenient to make it a subclass of the bicycle class
both tricycles and bicycles have a current speed and cadence
but because a tricycle is not a bicycle, it's unwise to publicly
tie the two classes together. It could confuse users, make the
tricycle class have methods (such as "change gears") that it
doesn't need, and make updating or improving the tricycle class
difficult.
Inheritance offers the following benefits:
Subclasses provide specialized behaviors from the basis of
common elements provided by the superclass. Through the
use of inheritance, programmers can reuse the code in the
superclass many times.
C++
Horizon
140
Programmers can implement superclasses called abstract
classes that define common behaviors. The abstract
superclass defines and may partially implement the
behavior, but much of the class is undefined and
unimplemented. Other programmers fill in the details with
specialized subclasses.
C++
Horizon
141
Provides for the reusability of code
One class can be derived from another
The class that inherits from another is the derived class
The class inherited from is the base class
An object of newly derived class is also an object of the base
class
C++
Horizon
142
B1
D
single inheritance
B2
D1
multiple inheritance
D2
D3
hierarchical inheritance
B
BD1
DB
BD2
D
D
multilevel inheritance
hybrid inheritance
Various Forms of Inheritance
C++
Horizon
143
Single Inheritance
Example :
class Base
{
int intVar;
public:
int i;
void funcB(unsigned x) { /* code */ }
};
class Derived : public Base
{
public:
void funcD(unsigned x) { /* code */ }
// belongs to the derived class
};
C++
Horizon
144
Member Accessibility
The members of a class can have the following visibility modes :
private : the default mode
visibility limited within class only
protected : visible within class
visible also within the subclass as private member
public : visible everywhere
class X {
private : //accessible to members and friends only
protected : //accessible to members and friends and
//to members and friends of derived classes only
public : //accessible to the general public
};
C++
Horizon
145
Derivation Modes
A derived class can be created by inheriting the base class in the
following modes :
private
protected
public
The default mode of derivation is private.
class der-class-name : visibility mode base-class-name
{
// mems of derived class
}
C++
Horizon
146
The following table shows the visibility of the inherited members
depending on the derivation mode.
Base
Derived class visibility
class
private
protected
public
visibility derivation
derivation
derivation
private
not inherited not inherited not inherited
protected private
protected
protected
public
protected
public
C++
private
Horizon
147
Multiple Inheritance
Syntax is as follows :
class D : visibility B1, visibility B2,
{
// mems of D
};
C++
Horizon
148
Virtual Base Classes
For the following example :
A1
A2
B1
B2
D
D inherits the properties from the following classes :
B1
B2
A1 through B1
A2 through B2
C++
Horizon
149
In case B1 and B2 have the same parent as shown in the following
figure :
grand parent
parent#2
parent#1
D
D1 inherits the properties of class grandparent through parent#1
as well as parent#2.
This is an ambiguous situation.
C++
Horizon
150
class G {
public :
int i ;
};
class P1:public G {
};
class P2:public G {
};
class D: public P1, public P2 {
};
main()
{
D d1 ;
cout<<d1.i ; // error due to ambiguity
}
C++
Horizon
151
It is essential to declare the common base class as a virtual base
class as shown below :
class G {
// mems of G
};
class P1: virtual public G {
};
class P2: public virtual G {
};
class D: public P1, public P2 {
};
This ensures that only one copy of the parent class G is available
in D.
C++
Horizon
152
Constructors In Derived Classes
As long as no base class constructor takes an argument, the
derived class need not have a constructor function.
If the base class contains at least one constructor with at least
one argument then it is compulsory for the derived class to have
a constructor and pass arguments to base class constructors.
When an object of derived class is created the base constructor
is executed before the derived constructor.
Example :
class D : public A {} ;
constructor execution order : A, D
C++
Horizon
153
In multiple inheritance, the base class constructors are executed
in the order in which the base classes appear in the declaration
of the derived class.
Example :
class D : public A1, public A2, public A3 {} ;
constructor execution order : A1, A2, A3, D
In case of multilevel inheritance the constructors are executed in
the order of inheritance.
Example :
class B : public A {} ;
class D : public B {} ;
constructor execution order : A, B, D
C++
Horizon
154
The constructors of virtual base classes are invoked before any
non virtual base class constructors.
Example :
class D : public A, virtual public B {} ;
execution order : B, A, D
The derived class must pass the initial values to the base class
constructors.
It does so for only its immediate parents.
The derived class constructors receive the entire list of
arguments and pass them to the base constructors in the order
in which they are declared in the base class.
C++
Horizon
155
Example :
class B1 {
int
i1, j1 ;
public :
B1(int x, int y) {
i1 = x, j1 = y ;
cout<<"B1:"<<i1<<" "<<j1<<"\n" ; }
};
class B2 {
int
i2, j2 ;
public :
B2(int x, int y) {
i2 = x, j2 = y ;
cout<<"B2:"<<i2<<" "<<j2<<"\n" ; }
};
C++
Horizon
156
class D : public B1, public B2 {
public :
D(int a, int b, int c):B2(c, a), B1(b, c)
{
cout<<"derived\n" ;
}
};
main()
{
D obj(1,2,3) ;
}
Output :
B1 : 2 3
B2 : 3 1
derived
C++
Horizon
157
class gp {
public :
gp(int a, int b) {cout<<a<<" "<<b<<endl ;} } ;
class p1 : virtual public gp {
public : p1(int x) : gp(1, 2) {cout<<"p1:"<<x<<"\n" ;} } ;
class p2 : virtual public gp {
public : p2(int x) : gp(12, 22) {cout<<"p2:"<<x<<"\n" ;} } ;
class ch : public p1, public p2 {
public : ch() : p1(6), p2(7), gp(3, 4) {cout<<"ch\n" ;} } ;
class gc : public ch {
public : gc() : gp(78, 672) {} } ;
main() { ch
chobj ;
gc
gcobj ;}
In this case the ch class passes arguments to its non immediate parent
constructor. This is because, when ch object is created, neither p1 nor p2
can do the argument passing to gp.
C++
Horizon
158
Hence :
First, virtual base classes are created before non-virtual base classes, which
ensures all bases get created before their derived classes.
Second, note that the p1 and p2 constructors still have calls to the gp constructor.
If we are creating an instance of ch, these constructor calls are simply ignored
because ch is responsible for creating the gp, not p1 or p2.
However, if we were to create an instance of p1 or p2, the virtual keyword is
ignored, those constructor calls would be used, and normal inheritance rules
apply.
Third, if a class inherits one or more classes that have virtual parents, the most
derived class is responsible for constructing the virtual base class. In this case,
ch (or gc) inherits p1 and p2, both of which have a gp virtual base class. ch (or
gc) , the most derived class, is responsible for creation of gp.
C++
Horizon
159
Initialization List in the Constructor Function :
Example :
class XYZ {
int a ;
int b ;
public :
XYZ(int i, int j) : a(i), b(2+j){ }
XYZ(int i) : b ( a+1 ) , a ( 3*i ) { }
};
main( ) {
XYZ
obj1(2, 3) ;
XYZobj2(6) ;
}
This assigns the value 2 to a and 5 to b for obj1 and 18 to a and 19
to b for obj2.
a can not be given in terms of b because data members are
initialized in the order in which they are created which is the
same as the order in which they are declared.
C++
Horizon
160
Containing Classes
An object can be a collection of many other objects.
Thus members of a class can be objects of some other classes.
Following are
Containership :
the
differences
Inheritance :
provides the is a
relationship between the
related classes
derived class object is
an extended base class
object
C++
between
Inheritance
and
Containership :
provides the has a
relationship between the
related classes
container class object
has a property displayed
by the contained class.
Horizon
161
Constructor Invocation Rules for
Container Classes
First all those member are created which are themselves
objects of other class.
The ordinary members are created afterwards.
Constructors of the member objects are executed before the
class constructor.
They are executed in the same order as the order in which
they are declared.
The arguments to the member objects constructor are passed
by the main class constructor just as in the case of
inheritance.
C++
Horizon
162
class mem {
int i_mem ; char
c_mem ;
public :
mem(int i, char c) : i_mem(i), c_mem(c) {cout<<"constr of mem\n" ;}
void disp( )
{cout<<"\ni_mem :::"<<i_mem<< c_mem :::"<<c_mem<<endl<<endl ;}} ;
class container {
int i_con ; char c_con ; mem mem_con ;
public :
container(int i, char c) : i_con(i), c_con(c), mem_con(i+10, c+10)
{cout<<"constr of con\n" ;}
void disp( ) {
cout<<"i_con :::"<<i_con<< c_con :::"<<c_con ;
mem_con.disp() ; } } ;
main( ) { container c1(10, 'A') ; [Link]() ;}
C++
Output :
constr of mem
constr of con
i_con ::: 10 c_con ::: A
i_mem ::: 20 c_mem ::: K
Horizon
163
Destructor Invocation Rules
The order of destruction is just the reverse of the order of
construction, i.e., the first constructed is the last destroyed.
C++
Horizon
164
Final class in C++
Class which Can't be inherited by other class, that class is called final class.
In C++ final class has to be logically created.
This can be done in two ways :
1) Object of the final class will be on heap
class final2
{
public:
static final2* Create() { return (new final2()) ; }
static void Des(final2 *p) {delete p ;}
private: ~final2() {}
};
int main() {
final2 *f ;
f = final2::Create() ; // Object only on Heap
final2::Des(f) ;
}
class child : public final2 {public: child(){ }
};
//Error will come because child class can not inherit final2 class - destructor of
the final2 class is private.
C++
Horizon
165
1) Object of the final class will be on stack
class temp{
private: ~temp( ) {cout << temp destr\n ;}
friend class Final ; // Due to friend , Final class can use private member functions of
// temp class
};
class Final: virtual public temp {
// Define all data members and functions as you want
public:
Final( ) { }
~Final( ) {cout << Final destr\n ; }
};
class Child : public Final {
public:
Child( ){ }
~Child( ) { }
};
Now this final class can't be inherited by other class because temp class is inherited
virtually by Final class. So when Child constructor is called, then first temp
constructor will be called. By Child constructor, we are calling temp
constructor. Child class can not call temp class's private destructor because
Child is not a friend of temp. Complier sees this as a violation of rules.
C++
Horizon
166
Pointers
Pointers to objects can be declared and initialized in just the
same way as pointers to ordinary variables.
They can also be initialized by using the new operator.
Example :
class C {
int i ;
public :
C(int x) : i(x) { } C() : i(9) {}
void f( ) { }
};
main( ) {
C *p1 = new C(9) ;
C o;
C *p2 = &o ;
}
C++
Horizon
167
Object pointers can be used to access the members using the
arrow operator.
*ptr can be used with the same meaning. Thus in the previous
example :
(*p1).f( ) is valid.
C++
Horizon
168
this Pointer
this is a pointer to that very object on which the
function is invoked.
Example :
class C {
int
i;
public :
C(int i)
{ this->i = i ; }
void disp( ) { cout<<i<<"\n" ; }
};
main( ) {
C obj(10) ;
[Link]() ;
}
C++
Horizon
169
Accessing Derived Class Objects
Through Base Pointers
A base class pointer can be used to point to a derived class
object.
If the base class does not contain any virtual functions then the
members referred by using such a pointer are those belonging to
the base class.
C++
Horizon
170
Class Conversion
An object of a derived class can be used as if it were an object of
its base class
Example :
class Base{..};
class Derived : public Base{.};
void main(void)
{
Derived d;
Base b;
b = d; // class conversion
}
An object of the base class cannot be used as an object of a
derived class
C++
Horizon
171
Overriding
A derived class can define a member with the same name as a
base class member
Referring to its name in the derived class, accesses the
member in the derived class
class Base {
int i;
public:
Base(int x){ i = x;} // constructor
};
class Derived :: public Base{
int i;
public:
Derived(int x){i = x};// Derived :: i
void CalculateTotal(void){int total = i + Base :: i; }
};
C++
Horizon
172
Exercises
Create the class relationships for the following fig :
Qualification
Staff
Teacher
Salary
C++
Typist
Regular
Horizon
Officer
Casual
173
Create two more classes Student and College. The College class
maintains lists of the teachers on a per subject basis. The listfunctions are to be used from a LinkedList class. Use the Teacher
class from the previous question. The Student class provides with
functions selectTeacher(subject, College) which allows him to
select a teacher for a specific subject. This function uses the
dispTeacherDetails(subject) function of the College class to see
the list of name and qualification of the teacher.
C++
Horizon
174
Input/Output Operations
C++
Horizon
175
Streams I/O Library
I/O library include file : <iostream>
<stdio.h> I/O function are still supported by C++
Problem with I/O of C
printf , scanf etc cannot be overloaded
New format specifiers for user defined types cannot be
added
Lack of type checking
Note : There are many independent implementations of the
stream I/O library and the set of facilities described here
is only a subset of the actual library.
C++
Horizon
176
ios
istream
ostream
iostream
C++
Horizon
177
Inserters
cout is a predefined output stream attached to the standard
output device
<< sends character to the output stream
The text string on the right is stored in the left stream
#include <iostream.h>
int main()
{ cout << Hello World ! }
Same precedence as the standard left shift operator
cout << x + y << \n ; // OK
cout << x & y << \n ; //Error
/* & has lower precedence than << */
cout << (x & y) << \n ; Horizon
// OK
C++
178
cerr is a predefined error output stream attached to the
standard error device.
C++
Horizon
179
Stream Extraction
Fetches data from an input stream
The >> (right shift) operator is overloaded as the extraction
operator
cin is associated with the standard input device
Three types of extractors
integral
floating point
character
C++
Horizon
180
All extractors skip leading white spaces
Integral extractors
Read input characters until any that cannot be part of the type
123x456 is read as 123 with input pointer at x
Floating point extractors
Read input characters until any that cannot be part of the type
123e-4x5 is read as 123.0e-4 with input pointer at x
Character extractor
Reads the text character in the input stream skipping leading
white spaces
char * or string extractors read all input characters, skipping
leading white spaces, upto the next white space
This string is read as This
C++
Horizon
181
Multiple data items can be read in a single statement by
chaining operators together.
Example :
void main(void) {
char c ; int i ; float f;
char * buf[30];
cin >> c >> i >> f ;
}
void main(void) {
int i ; char c ; double
cin>>i>>c>>d ;
cout<<i<<endl ;
cout<<c<<endl ;
cout<<d<<endl ;
}
C++
d;
INPUT : 23x1.2
OUTPUT : 23
X
1.2
Horizon
182
I/O Formatting
Special methods are defined for formatting stream objects.
The width method specifies I/O width
For cin , only the specified numbers of characters are read
For cout , the output is displayed right justified in a field of
the specified width
If the length of input is greater than the current width , the
entire value is displayed for cout
Example :
char buf[21];
[Link](20); //only 20 characters read at a time
cin >> buf ;
int x = 1;
[Link](5);
cout << x ; /* x is displayed right justified in a field
five characters long */
C++
Horizon
183
The precision method sets the number of digits after the decimal
point
Involved when a float or double is displayed
Invoked with or without an int argument
Example :
void main(void) {
float pi = 3.14159 ;
[Link](3);
cout << pi ; }
Output : 3.14
C++
Horizon
184
The fill method sets the character used for padding extra space
when calling width
<space> is default fill character
Invoked only if the value set using width is greater than the
length of the inserted value
Example :
void main ( ) {
int x = 10 ;
[Link](0);
[Link](5);
cout<<x ; }
Output : 00010
C++
Horizon
185
Format Flags
Streams recognize flags to control format of input and output
left , right , internal
Only one of these may be set at any time
If left is set , inserted data is left justified
If internal is set , the sign of a numeric is left justified and
the value is right justified
Extra space in a field of set width is filled with the fill
character
If two of these are unset , the third is automatically set
float pi = 3.14159 ;
[Link](3) ;
[Link](8) ;
[Link](#) ;
[Link](ios::left) ;
cout<<pi ;
C++
OUTPUT :
3.14####
Horizon
186
dec , oct , hex
Only one of these may be set at any time
These control the base in which numbers are displayed
On extraction, the integral values are assumed and
interpreted to be in the set format
dec is set by default
showbase
Prefaces integral insertions with the base indicators used
with C++ constants
If hex is set , 0x is inserted in front of any integral
insertion and if oct is set then 0is inserted
This flag is not set by default
showpos
A + sign is inserted before any integral insertion
Remains unset by default
C++
Horizon
187
uppercase
All letters in numeric insertions will be converted to upper
case
Unset by default
showpoint
Forces the display of trailing zeros and decimal points in
float and double insertion
Unset by default
scientific , fixed
If scientific is set , floating point values are inserted using
scientific notation
One digit before the decimal point , precision digits after it ,
an e and the exponent value are present for the scientific
notation
If fixed is set , the value is inserted using decimal notation ,
with precision digits following the dicimal point
By default , scientific notation is used when the exponent is
less than -4 or greater than precision
C++
Horizon
188
unitbuf
The stream is flushed after every insertion
The flag is unset by default
stdio
Flushes the stdout and stderr devices defined in the stdio.h
Unset by default
Flags are stored as bits in a long member of every stream
Flags are set, unset and read by the following stream methods
long flags( )
returns the current format flags
long setf(long f)
sets the format flags to f
returns the previous flag value
long unsetf(long f)
unsets the flags that are set in f
returns the previous flag value
C++
Horizon
189
The bit values for the flags are members of an unnamed
enumerated type defined in the ios class
Flags can be ored (|) together to set/unset several flags in one
statement
C++
Horizon
190
Example :
#include <iostream.h>
void main ( )
{
double pi = 3.1415927 ;
int x = 1234 ;
// set new flags
[Link]( ios :: hex | ios :: showbase | ios :: uppercase | ios ::
scientific) ;
cout << x= << x <<pi =<< pi << endl;
//reset to original flag values
[Link](ios :: hex | ios :: showbase | ios :: uppercase | ios ::
scientific);
}
C++
Horizon
191
Manipulators
Manipulators
Change the format flags and values of a stream
Are set in the stream itself
Example :
int x = 1 , y = 2 ;
// sets the width
cout << setw(5) << x << setw(6) << y;
Predefined manipulator :
hex , dec ,oct
change the base of inserted or extracted integral values
The default for stream in dec
Example :
int i = 24 ; cout<<hex<<i<<endl ;
ios
Extract white space characters
endl
Inserts newline character
C++
Horizon
192
ends
Inserts a NULL character
Usually used to terminate a string
flush
Forces all insertions in the stream to be physically written to
the appropriate device
setfill(char f)
Changes the fill character to f
The default fill character is <space>
setw(int w)
change the field width to w
Is valid only for text insertion
Default field width is 0
C++
Horizon
193
setprecision(int p)
Sets the precision for floating point insertions to p
The default precision is 6
setiosflags(long f)
Sets the flags that are set in f
resetiosflags(long f)
Unsets the flags set in f
Header files
Include iostream.h and iomanip.h
C++
Horizon
194
Other input streams function
tie(ostream)
Attaches an output stream to an input stream
Output stream is flushed before extractions on the input
stream
[Link](cout) ; // flushes cout before cin
[Link](0) ; // unties cin from cout
ignore(int i)
Unconditionally ignores the next i characters for cin
[Link](5);
peek( )
The next character in the input stream can be looked at
without actually fetching i
char ch = [Link]()
C++
Horizon
195
putback(char ch)
Returns the last character that has been fetched to the input
buffer
An error may occur if the stream cannot accept the putback
character
get( )
Reads the next character from the input stream
char ch = [Link]( );
get() is overloaded with other version
get(char *str, int len, char delim = \n)
Fetches characters from the input stream into the array str
fetching is stopped if the len characters have been fetched
Fetching is also stopped if delim character is encountered
Next read occurs at the delimiter
C++
Horizon
196
get(char &ch)
Fetches the next character in the stream and stores it in ch
getline(char *str, int len, char delim = \n)
similar to get(char *, int, char)
Extracts the terminator also
Next read occurs after the delimiter
put(char ch)
A single character is written to an output stream without
translation
C++
Horizon
197
C++ Stream Classes
Declared in iostream.h
Stream classes for console operations are :
ios (General input/output stream class)
Contains basic facilities that are used by all other input
and output classes
It also contains a pointer to a buffer object (streambuf
object)
Declares constants and functions that are necessary for
handling formatted input and output operations
istream (Input stream)
Inherits the property of ios class
Declares input function such as get(),getline() and read()
Contains overloaded extraction operator >>
C++
Horizon
198
ostream (Output stream)
Inherits the property of ios class
Declares output function such as put() and write()
Contains overloaded insertion operator <<
iostream (Input/output stream)
Inherits the property of ios , istream and ostream class
through multiple inheritance and thus contains all the
input and output function
streambuf
Provides an interface to physical devices through buffers
It acts as a base for filebuf class used in files
C++
Horizon
199
Classes for File Operation
C++ I/O system contains set of file stream classes in fstream.h
Includes
filebuf
Its purpose is to set the file buffers to read and write
Also contains open() and close() method
fstreambase
Provides operations common to the file streams
Serves as base for fstream,ifstream and ofstream
Contains open() and close() method
C++
Horizon
200
ifstream class
Provides input operations
Contains open() with default input mode
Inherits the functions get(),getline(),read(),seekg() and
tellg() from istream
ofstream class
Provides output operations
Contains open() with default output mode
Inherits the functions put(),seekp(),write() and tellp()
from ostream
fstream class
Provides support for simultaneous input and output
operations
Contains open()
Inherits the functions from istream and ostream classes
through iostream
C++
Horizon
201
Opening A File
For opening a file
Create the file stream object
Link the file stream with filename
Two ways to open file
Using constructor function of the class
Create a file stream object to manage the stream using
appropriate class
Initialize the file object with the desired filename
Example :
ofstream outfile([Link]) ;
This statement opens the file [Link] and attaches it to
output stream outfile
This method is more appropriate when we use only one
file in the stream
C++
Horizon
202
Using the member function open() of the class
Create a file stream object to manage the stream using
appropriate class
Call open() method of stream object with the desired
filename
Example :
ofstream outfile ;
// creates stream
[Link]([Link]) ; // connects
This statement opens the file [Link] and attaches it
to output stream outfile
This method is used when we want to manage multiple
files using one stream object.
C++
Horizon
203
open() member function can take one OR two arguments
open(filename , filemode)
filename , specifies the file to be opened
filemode , specifies the purpose for which file is opened
Prototype for open() contains default values for filemode
argument
ios::in for ifstream functions meaning open for reading only
ios::out for ofstream functions meaning open for writing only
C++
Horizon
204
File mode constants are defined in the class ios
Filemode parameter can take one or more filemode constants
ios::app
Appends to end of file
ios::ate
Go to end of file on opening
ios::in
Open file for reading only
ios::nocreate Open fails if file does not exists
ios::noreplace Open fails if file already exists
ios::out
Open file for writing only
ios::trunc
Delete content of file if it exists
C++
Horizon
205
File Pointers & Manipulations
Each file has two file pointers associated with it
Input pointer (get pointer)
Output pointer (put pointer)
Input pointer is used for reading the content from file
Output pointer is used for writing to the file
Pointer advances automatically on each input/output operation
File stream classes provide functions for file pointer
manipulation
C++
Horizon
206
Functions for manipulating get pointer :
seekg(offset , refposition)
Moves get pointer to the specified location.
Parameter offset represents no of bytes the file pointer is to be
moved from the location specified by the parameter refposition
refposition can be ios::beg,ios::cur or ios::end
tellg()
Gives the current position of get pointer
Functions for manipulating put pointer :
seekp(offset , refposition)
Moves put pointer to the specified location.
Parameter offset represents no of bytes the file pointer is to be
moved from the location specified by the parameter refposition
refposition can be ios::beg,ios::cur or ios::end
tellp()
Gives the current position of put pointer
C++
Horizon
207
A simple example :
#include <string.h>
#include <iostream> #include <fstream>
using namespace std ;
main() {
char name[30] ; float
cost ;
ofstream out("TEST") ;
cout<<"Enter name:" ; cin>>name ; out<<name<<endl ;
cout<<"\nEnter cost:" ; cin>>cost ; out<<cost<<"\n" ;
[Link]() ;
strcpy(name, "") ;
cost = 0 ;
ifstream in("TEST") ;
in>>name ; in>>cost ;
cout<<"\nName : "<<name ;
cout<<"\nCost : "<<cost ;
}
C++
Horizon
208
Binary read, write to files : (The binary format is more accurate for storing
numbers and always faster for saving data as there are no conversions involved in
the process.)
#include <string.h> #include <iostream> #include <fstream>
class MyClass {
int intMem ; char chMem ; char chArr[5] ;
public :
int pubInt ; MyClass() {}
MyClass(int i){ intMem = 1 ;chMem = 'w' ;strcpy(chArr, "good") ;
pubInt = 20 ;}
void f(){} friend ostream &operator <<(ostream &s, MyClass c) ;} ;
ostream & operator << (ostream &s, MyClass c) {
s<<"\nIntMem:"<<[Link] ;s<<"\nCharMem:"<<[Link] ;
s<<"\nchArr : "<<[Link] ;s<<"\npubInt : "<<[Link]<<endl ;
return s ;}
main() {
MyClass
o1(10), o2 ; cout<<o1 ; cout<<o2 ;
fstream
file("TEST", ios::in|ios::out) ;
[Link]((char *)&o1, sizeof(o1)) ;
[Link](0,ios::beg) ; [Link]((char *)&o2, sizeof(o2)) ;
cout<<o1 ;
cout<<o2 ;
[Link](0,ios::end) ;
file<<o2 ; [Link]() ;}
C++
Horizon
209
Stream States
Every stream has a state associated with it.
Errors and non-standard conditions are handled by setting and
testing this state appropriately.
The stream state can be examined by operations on class ios.
Example :
fstream file(Temp) ;
[Link]() ; //end of file seen
[Link]() ; //next operation will fail
[Link]() ; //stream corrupted
[Link]() ; //next operation might succeed
C++
Horizon
210
String Stream
A stream can be attached to an array of characters in main memory.
A rough sketch of the available classes is given below :
istrstream
Contains open() with default input mode
Provides input operations
ostrstream
Contains open() with default output mode
Provides output operations
strstream
Contains open()
Provides support for simultaneous input and output operations
C++
Horizon
211
Example :
#include <string.h> #include <iostream> #include <strstream>
class MyClass {
int intMem ; char chMem ; char
chArr[5] ;
public :
int pubInt ; MyClass() {}
MyClass(int i){ intMem = 1 ;chMem = 'w' ;strcpy(chArr, "good") ;
pubInt = 20 ;}
void f(){} friend ostream &operator <<(ostream &s, MyClass c) ;} ;
ostream & operator << (ostream &s, MyClass c) {
s<<"\nIntMem:"<<[Link] ;s<<"\nCharMem:"<<[Link] ;
s<<"\nchArr : "<<[Link] ;s<<"\npubInt : "<<[Link]<<endl ;
return s ;}
main() {
MyClass
o1(40), o2 ;
cout<<o1 ;
cout<<o2 ;
char *p = new char[50] ;
strstream
str(p, 50) ;
[Link]((char *)&o1, sizeof(o1)) ;
cout<<o1 ; cout<<o2 ;
[Link]((char *)&o2, sizeof(o2)) ;
cout<<o2 ;}
C++
Horizon
212
Exercises
First write a program that creates a file of floating point numbers in
binary format. Then write a program that reads this file of floating point
numbers, makes complex numbers out of pairs of numbers read, and
displays the complex numbers.
Write a miniaturestream I/O system that provides classes istream,
ostream ifstream, ofstream providing functions such as operator<<() and
operator>>() for integers and operations such as open() and close() for
files. Use exceptions, rather than, state variables to communicate error
conditions.
Write a class CpFileToFile which reads source and destination file names
and provides for a function for copying from source to destination. Also
overload subscript operator to start copying from character provided as
input to this operator function. The function must return number of
characters hence copied from source to destination.
C++
Horizon
213
Templates
C++
Horizon
214
Templates enable us to define generic classes or functions
Templates make it possible to use a single function or a class
definition for a variety of data types i.e., to define a family of
functions or classes.
Template is defined with a parameter that would be replaced by
specific data type at the time of actual use of class or function.
A template can be considered as a type of a macro.
Templates can be :
function templates or
class templates.
C++
Horizon
215
The need for template functions :
To find minimum of two numbers, depending on the data type
of the numbers, the functions have to be declared.
Thus if the numbers are of int type, a function as given below
is required :
int min(int a, int b) ;
For two float type numbers the following function is required :
float min(float a, float b) ;
That is, though the functionality remains the same, separate
functions are required for separate data types.
This repetition of code can be avoided by using template
functions.
C++
Horizon
216
Function Templates
Syntax :
template <class T>
return_type function_name(args_list)
{ }
Example :
template <class T>
void Swap(T &x1, T &x2) {
T temp = x1 ;
x1 = x2 ; x2 = temp ; }
main( ) {
int i = 10 , j = 80 ;
Swap(i, j) ;
char c = A , d = B ;
Swap(c, d) ;}
C++
Horizon
217
class A {
int a1, a2 ;
public : A(int x) : a1(x) , a2(2*x) { }
int f( ) {return a1 ;} } ;
class B {
char b1 ;
public : B(char x) : b1(x) { }
char f( ) {return b1 ;} } ;
template <class T1, class T2>
T2 anything(T1 a , T1 b, T2 c) {
return T2(a+b+c.f( ) ) ;
}
main( ) {
int x = 9 , y = 10 ;
char c = Z , d = A ; A a1(60) ; B b1(K) ;
A a_obj = anything (x , y, a1) ;
B b_obj = anything (c , d, b1) ;
}
C++
Horizon
218
Performance
The compiler does not compile any code when it encounters the
template function definition.
When the template function is invoked then at that point the
compiler generates a specific version of the template function
corresponding to the argument types.
Finally at this point compiler generates the function call code.
This it does every time the compiler comes across a call
corresponding to a template function.
Use of templates does not help save memory because all the
different versions of the function are ultimately generated.
C++
Horizon
219
The advantage is that the code can be written with the generic
version only.
Thus templates provide reuse of the source code in contrast to
inheritance and containership which provide reuse of the object
code.
Templates can significantly reduce source code size and
increase code flexibility without reducing type safety.
C++
Horizon
220
A template function may be overloaded either by template functions
or ordinary functions of its name.
The overloading resolution is accomplished as follows :
Call an ordinary function that has an exact match.
Call a template function that could be created with an exact
match.
Try normal overloading resolution to ordinary functions and call
the one that matches.
An error is generated if no match is found.
No automatic conversions are applied to arguments on template funcs.
Example :
template <class T> T max(T a, T b) {return a>b?a:b ;}
void f(int a, int b, char c, char d) {
int m1= max(a, b) ; //max(int a, int b)
char m2 = max(c, d) //max(char c, char d)
int m3 = max(a, c) } //error c is a char and can not be converted to int
C++
Horizon
221
Thus a template function can be overridden to perform a specific
processing for a particular data type.
Example :
template <class T>
void f(T a) {
cout<<"inside template "<<a<<endl ; }
void f(char c) {
cout<<"non template "<<c<<endl ; }
main()
{
f(10) ;
f('A') ;
f(10.8) ;
}
C++
Output :
inside template 10
non template A
inside template 10.8
Horizon
222
template <class T>
T max (T a, T b) {
cout<<"templ\n" ;
return a>b ?a:b ;
}
void main() {
int a = 90, b = 80 ;
charc = 'a', d = 'n' ;
cout<<max(a, b)<<endl ;
cout<<max(c, d)<<endl ;
cout<<max(a, c)<<endl ; // error : cannot generate max(int, char)
}
But if the following is added :
int max (int a, int b) {
cout<<specl\n" ;
return a>b ?a:b ;
}
the first and the last function calls are resolved to the special version of the code.
C++
Horizon
223
Each template argument of a function template must affect the type
of the function by affecting at least one input argument type of
functions generated from the template.
This ensures that functions can be selected and generated based on
their arguments.
Example :
template<class T> void f1(T) ;
// fine
template<class T> void f2(T*) ;
// fine
template<class T> T f3(int) ;
// error
template<class T, class C> void f4(T t) ;
// error
template<class T> void f5(const T&, complex) ; // fine
template<class T> void f6(Vector<List<T>>) ; // fine
C++
Horizon
224
class A {int
a1, a2 ;
public : A(int x) : a1(x) , a2(2*x) { }
int f( ) {return a1 ;}} ;
template <class T1, class T2>
T2 anything(T1 a , T1 b, T2 c) {return T2(a+b+c.f( ) ) ;}
char anything(char b) { return b ; }
char anything(int a, int b, char c) { return 'a' ; }
main( ) {
int
x = 9, y = 10 ;
long p = 10, q = 11 ;
char c = 'Z' , d = 'A' ;
A
a1(60) ;
A a_obj = anything (p, q, a1) ;
cout<<anything('q') ;
cout<<anything(1, 2, 'q') ;
}
C++
Horizon
225
Class Templates
Example :
template <class T>
class stack {
T * arr ; int top ;
public : stack(int size ) {arr = new T[size] ; top = -1 ;}
void push(T t) ;
T pop( ) ; } ;
Syntax :
template <class T>
class class_name{ } ;
template <class T>
void stack <T> :: push(T t) { }
template <class T>
T stack <T>::pop( ) { }
T may be any data type:
built in
user defined
-structures
-classes
main( ) {
stack <int> st_int(10) ;
stack <char>
st_char(20) ; }
the keywords class and typename in a template parameter declaration
C++
Horizon
226
In the previous example, T is the template parameter and int or char (used at the
time of stack object creation) are the template arguments.
To use the default value of a template parameter, the corresponding template
argument must be omitted :
template <class T = int>
class stack {
T * arr ; int top ;
public : stack(int size ) {
arr = new T[size] ;
top = -1 ;
cout<<sizeof(T)<<"\n" ;
}
void push(T t) ;
T pop( ) ; } ;
template <class T> void stack <T> :: push(T t) { }
Output :
4
1
template <class T> T stack <T>::pop( ) { }
main( ) {
stack <> st_int(10) ;
stack <char>
st_char(20) ;
// this would generate an error -- stack var(8) ;
}
C++
Horizon
227
Class template can take default arguments.
The values of these arguments then become compile time constants
for that particular instantiation of the template.
template <class T, int def = 10>
class stack {
T * arr ;
int top ;
public :
stack(int size = def) {
cout<<size<<endl ;
arr = new T[size] ; top = -1 ;}
void push(T t) { }
T pop( ) { } } ;
main( ) {
stack <int>
st_int(20) ;
stack <char>
st_char ;
stack <char, 30>
st_char1 ; }
Output :
20
10
30
A new template can be inherited from an existing one.
It is a good practice to debug a particular template as a
function / class with specific data types.
C++
Horizon
228
Templates and multiple-filed projects
From the point of view of the compiler, templates are not normal
functions or classes.
They are compiled on demand, meaning that the code of a template
function is not compiled until an instantiation with specific template
arguments is required.
At that moment, when an instantiation is required, the compiler
generates a function specifically for those arguments from the
template.
For large code, it is usual to split the code of a program in different
source code files.
In these cases, the interface and implementation are generally
separated.
Taking a library of functions as example, the interface generally
consists of declarations of the prototypes of all the functions that can
be called.
C++
Horizon
229
These are generally declared in a "header file" with a .h extension,
and the implementation (the definition of these functions) is in an
independent file with c++ code.
Because templates are compiled when required, this forces a
restriction for multi-file projects: the implementation
(definition) of a template class or function must be in the same
file as its declaration. That means that we cannot separate the
interface in a separate header file, and that we must include
both interface and implementation in any file that uses the
templates.
Since no code is generated until a template is instantiated when
required, compilers are prepared to allow the inclusion more
than once of the same template file with both declarations and
definitions in a project without generating linkage errors.
C++
Horizon
230
Friends and Templates
A friend function of a template is not implicitly a template function.
Example :
template <class T> class task{
//
friend void next_time() ;
friend task<T> * preempt(task <T>*) ;
friend task * prmt(task *) ; // error
//
};
The function next_time is a friend to all task classes.
The function preemt is an appropraitely typed function for the specific
version of the class task.
The function prmt is wrongly declared because there is no type task,
but only specific template types like task<int>, task<record>, etc.
C++
Horizon
231
When a non-template class declares a template function as a friend, it
does so for a specific version of the template function.
Example :
class a {
int ai ;
template <class t>friend void func(t) ;
public :
a():ai(99){}
};
template <class T>
void func(T i) {
a obj ;
cout<<"func::"<<[Link]<<"\n" ;
}
main() {
int i ;
func(i) ;
func(99.9) ;
C++
Horizon
232
Static Members / Functions and
Templates
Each template class or function generated from a template has its own
copies of any static variables.
Example :
template <class T> class X {
static T s ;
//
};
X<int> aa ; X<char *> bb ;
Here X<int> has a static member s of type int and X<char *> has a
static member s of type char*.
C++
Horizon
233
Inheritance for Template Classes
The following code shows how can a template base class be used to
create a derived class :
template <class T>
class stack {
T * arr ; int top ;
public : stack(int size) {
cout<<size<<endl ; arr = new T[size] ; top = -1 ; }
void push(T t) { }
T pop( ) { }
};
template <class T1, class T>
class sp_stack : public stack<T> {
public : sp_stack() : stack<T>(99) {}
void f(T1 t1, T t2) {cout <<t1<<" "<<t2<<endl ;} } ;
void main( ) {
stack <char> st_char1(30) ;
sp_stack <int, double> spint ;
int i = 320 ;double d= 1.2 ;
spint.f(i, d) ; }
C++
Horizon
234
The following code shows handling of default arguments while
extension of a template base class :
template <class T, int def = 90>
class stack { T * arr ; int top ;
public : stack(int size = def) {
cout<<size<<endl ; arr = new T[size] ; top = -1 ; }
void push(T t) { }
T pop( ) { } } ;
template <class T1, class T, int def1 = 10>
class sp_stack1 : public stack<T, def1> {
public : void f(T1 t1, T t2) {cout <<t1<<" "<<t2<<endl ;} } ;
template <class T1, class T>
class sp_stack2 : public stack<T> {
public : void f(T1 t1, T t2) {cout <<t1<<" "<<t2<<endl ;} } ;
void main( ) {
stack <int> st_int(20) ; stack <char, 30>
st_char1 ;
sp_stack1 <int, double>
spint ;
sp_stack1 <char, double, 33>
spchar ;
sp_stack2<char, char> cpCharChar ;
int i = 320 ;double d= 1.2 ;
spint.f(i, d) ; }
C++
Horizon
Output :
20
30
10
33
90
320 1.2
235
Derivation and Templates
Two types generated from a common template are different and have no
inheritance relationship, unless their template arguments are identical.
This feature is depicted in the following code :
template <class T> class stack { } ;
class shape { } ;
class circle : public shape { } ;
void main( ) {
stack<int> sint1 ; stack<int> sint2 ; stack<double> sdouble ;
sint1 = sint2 ;
sint1 = sdouble ; // error
stack<circle *> cirPtr1 ; stack<circle *>
cirPtr1 = cirPtr2 ;
shPtr1 = cirPtr1 ; // error
circle
*cptr ;
shptr = cptr ;
}
C++
shape
cirPtr2 ; stack<shape *> shPtr1 ;
*shptr ;
Horizon
236
Class Template and Partial Template Specialization
Class Template Specialization :
In many cases when working with templates, it is required that one generic
version is written for all possible data types and leave it at that - every vector may
be implemented in exactly the same way.
The idea of template specialization is to override the default template
implementation to handle a particular type in a different way.
Example :
While most vectors might be implemented as arrays of the given type, you might
decide to save some memory and implement vectors of bools as a vector of
integers with each bit corresponding to one entry in the vector.
So two separate vector classes would be required :
The first class might look like as follows :
C++
template <class T>
class vector {
private:
T* vec_data; // the data is stored as block of dynamically allocated memory
int length; // number of elements used
int vec_size; // actual size of vec_data
};
Horizon
237
But when it comes to bools, it might not be required to do this because most
systems are going to use 8 or 16 or 32 bits for each boolean type even though all
that's required is a single bit.
So the boolean vector can be made to look a little bit different by representing
the data as an array of integers whose bits can be manually manipulated.
To do this, it is still needed to specify that something similar to a template is
being used, but this time the list of template parameters will be empty :
template <>
and the class name is followed by the specialized type, so that the template
would look like this :
template <>
class vector <bool> {
private:
unsigned int *vector_data;
int length;
int size; };
It is perfectly reasonable if the specialized version of the vector class has a
different interface (set of public methods) than the generic vector class although they're both vector templates, they don't share any interface or any
code.
C++
Horizon
238
Thus the idea of C++ class template specialization is similar to function template
overloading.
This can make the template code for certain data types to be fixed.
Once the template is specialized, all the member functions should be declared and
defined for the specific data type.
Example :
template<>
class MyQueue<double> {
std::vector<double> data;
public : void Add(double const &); void Remove(); void Print();
};
template <> void MyQueue<double> ::Add(double const &d) {
data.push_back(d);
}
C++
Horizon
239
Partial Template Specialization :
Partial template specialization allows the programmer to specialize only some
arguments of a class template, as opposed to explicit specialization, where all
the template arguments are provided.
Example :
template <class A, class B>
class Pair {
private: A a; B b;
public:
Pair(A aa, B bb):a(aa),b(bb) {}
void display() {
cout << a << ' ' << b << endl; }
};
To use this class, the template parameters can be declared explicitly when an
instance of the class is declared :
Pair<double,double> doubledouble(1.2,2.3);
[Link]();
C++
Horizon
240
Heres a partial template specialization of the Pair class. It has special code
when the second parameter is an int :
template <class A>
class Pair<A, int> {
private: A a; int b;
public:
Pair(A aa, int bb):a(aa),b(bb*bb) {}
void display() {
cout << a << ' ' << b << endl;
}
};
Using the partially-specialized template is no different from using the original
template :
Pair<double,int> doubleint(2.2,3);
[Link]();
C++
Horizon
241
Exercises
1) Write a template class to implement a stack.
Write a template class to create a map to hold key-value pairs.
The key as well as the value could be objects of any class.
Overload the subscript operator to take the key as an input and
return the value as the output. The value returned should be
the one corresponding to the one stored against the key provided
as input to the function.
C++
Horizon
242
Use this map class to hold a map of teacherName and phNo in
the College class. Let the Student class have the following
methods :
class Student {
public : void dispTeacherDetails(subject, College) { }
void findTeacher(subject , College) {
dispTeacherDetails(subject, College) ;
// select teacher name
[Link](teachName) ;
getTeachPhNo(char * teachName) ;
}
};
Now the College class has a method findPhNo(char *) which
uses the overloaded subscript operator that returns the teachers
phNo.
C++
Horizon
243
Exception Handling
C++
Horizon
244
Exceptions refer to unusual conditions in the program.
The purpose of exception handling is to provide means to
detect and report errors such that necessary action can be taken.
This mechanism involves the following tasks :
Find the problem hit the exception
Inform that an exception has occurred throw the exception
Receive the error information catch the exception
Take corrective actions - handle the exception
Keywords used :
throw
try
catch
C++
Horizon
245
Exceptions could be
synchronous :- errors such as
out-of-range index
falling short of memory
Inability to open a file
asynchronous :- caused by events beyond the control of the
program, eg, keyboard interrupts
The exception handling mechanism is designed to handle the
synchronous exceptions.
C++
Horizon
246
class stack {
int
arr[2], top ;
public : stack( ){top = -1 ;}
class full{ } ; class empty{ } ;
void push(int i) {
if (top == 1) throw full() ;
top++ ;arr[top] = i; }
int pop( ) {
if(top == -1) throw empty() ;
else {top-- ; return 1 ;} }
};
main( ) {
stack s ;
try{ [Link](1) ;cout<<"1\n" ;
[Link](2) ;cout<<"2\n" ;
[Link](3) ;cout<<"3\n" ;
} catch(stack::full f){cout<<"full\n" ;} }
C++
Horizon
OUTPUT : 1
2
full
247
The push and pop functions throw the objects of the relevant classes.
Following statements summarize the working of the exception
handling mechanism :
Code executes normally outside the try block.
When a statement in a try block causes an error in a member
function then that member function throws an exception.
Control then transfers to the catch block following the try block.
Once the catch block is executed the control jumps to the first
statement after the catch block.
If for an exception there is no matching exception handler on the
call chain where the exception is generated, the program will be
terminated.
C++
Horizon
248
If an exception is thrown before the constructor completes
execution then the associated destructor will not be called for
that object.
When an exception is thrown, a destructor is automatically
called for any object that was created by the code up to that
point in the try block.
Exceptions impose an overhead in terms of program size and
(when an exception occurs) in time. So we should not try to
overuse it.
C++
Horizon
249
An exception is caught by specifying its type.
What is thrown is not a type but an object.
If we need to transmit extra information from the throw point to the
handler, we can do so putting data into that object.
Example :
class Vector {
int sz ; int *p ;
public : class Range {
public : int index ;
Range(int i) : index(i) { } } ;
Vector (int s) : sz(s) { /* initialize p */}
int & operator[ ] (int i) {
if (o <= i && i < sz) return p[i] ;
throw Range(i) ; }} ;
void f(Vector &v) {
//
try { do_something(v) ;}
catch (Vector :: Range r) {cerr<<bad index ::<<[Link]<<endl ;}
};
C++
Horizon
250
For templates we have a choice when naming an exception.
Each class generated can have its own exception class as shown
below :
run once with ai.f(10) ; ad.f(1) ;
template <class T> class Allocator {
and once with ai.f(1) ; ad.f(10) ;
public : class Exhausted { } ;
T * get( ) { }
void f(int i) { cout<<"in f\n" ; if(i == 10) throw Exhausted() ;} } ;
void f(Allocator <int> &ai, Allocator <double> &ad) {
try { ai.f(10) ; ad.f(1) ; }
catch (Allocator <int> :: Exhausted) { cout<<"int ver\n" ; }
catch (Allocator <double>:: Exhausted) { cout<<"double ver\n" ; }
}
void main() {
Allocator<int> aInt ;
Allocator<double> aDbl ;
f(aInt, aDbl) ;
}
C++
Horizon
251
Alternately, an exception can be common to all classes generated
from the template :
class Allocator_Exhausted { } ;
template <class T> class Allocator {
public :
T * get( ) { }
void f(int i) { cout<<"in f\n" ;if(i == 10) throw Allocator_Exhausted() ;}
};
void f(Allocator <int> &ai, Allocator <double> &ad) {
try { ai.f(1) ; ad.f(10) ; }
catch (Allocator_Exhausted) { cout<<"exc\n" ; }
}
void main() {
Allocator<int> aInt ;
Allocator<double> aDbl ;
f(aInt, aDbl) ;
}
C++
Horizon
252
The following example shows that if an exception is thrown
before the constructor completes execution then the associated
destructor will not be called for that object.
class Cl {
public : class exc{} ;
Cl( ) { cout<<"in constr\n" ;
throw exc() ;
cout<<"exception thrown\n" ; }
~Cl( ) { cout<<"in destr\n" ; }
OUTPUT
in constr
exception caught
};
main( )
{ try
{ Cl obj ; }
catch(Cl::exc E) {cout<<"exception caught" ;}
}
C++
Horizon
253
The following example shows that when an exception is thrown, a
destructor is automatically called for any object that was created by the
code up to that point in the try block.
class Cl {
public : class exc{} ;
Cl( ) { cout<<"in constr\n" ;
throw exc() ;
cout<<"exception thrown\n" ; }
~Cl( ) { cout<<"in destr\n" ; } } ;
class CC {
public : CC() {cout<<"constr of CC\n" ;}
~CC() {cout<<"destr of CC\n" ;} } ;
OUTPUT
constr of CC
in constr
destr of CC
exception caught
main( ) {
try
{ CC o1 ;
Cl obj ; }
catch(Cl::exc E) {cout<<"exception caught" ;}
}
C++
Horizon
254
class full { public: void disp() {cout<<"full\n" ;}} ;
class subFull: public full{ } ;
class stack {
int
arr[2], top ;
public :
stack( ) {top = -1 ;}
void push (int i){
if (top == 1) throw subFull() ;
else {top++ ;arr[top] = i;}}
int pop( ) {
if(top == -1) throw empty() ;
else {top-- ; return 1 ;} }
};
This exception is caught in the
main( ){ stack s ;
first catch block i.e., by the
try {
[Link](1) ;cout<<"1" ;
base exception handler
[Link](2) ;cout<<"2" ;
[Link](3) ;cout<<"3" ;}
catch(full f){ [Link]();}
catch(subFull sf){cout<<"in subfull\n" ;}
}
C++
Horizon
255
class full { public: void disp() {cout<<"full\n" ;}} ;
class BigNum { public: void disp() {cout<<"tooooooooo big num\n" ;}} ;
class stack {
int
arr[2], top ;
public : stack( ){top = -1 ;}
void push(int i) {
if (i > 100) throw BigNum() ;
if (top == 1) throw full() ;
else {top++ ;arr[top] = i;} } } ;
void f1(stack s) {
try{
[Link](101) ;cout<<"1" ;
[Link](2) ;cout<<"2" ;
[Link](3) ;cout<<"3" ;
}
catch(BigNum big){[Link]();}}
main( ) {
push throws two exceptions
one of them is handled in main
and the other is handled in f1()
stack
s;
try { f1(s) ;}
catch(full f){ [Link]();}
}
C++
Horizon
256
class full { public: void disp() {cout<<"full\n" ;}} ;
class BigNum { public: void disp() {cout<<"tooooooooo big num\n" ;}} ;
class stack { int
arr[2], top ;
public : stack( ){top = -1 ;}
try inside another catch
class empty{ } ;
void push(int i){
if (i > 100) throw BigNum() ;
if (top == 1) throw full() ;
else {top++ ;arr[top] = i;}}
int pop( ) { if(top == -1) throw empty() ;
else {top-- ; return 1 ;} }} ;
void f1(stack &s) {
[Link](1) ;cout<<"1\n" ; [Link](2) ;cout<<"2\n" ;
[Link](3) ;cout<<"3\n" ; [Link]() ;cout<<"4\n" ;}
void main( ){
stack
s;
try {
f1(s) ; }
catch(full f) {
[Link]();
try{
[Link]() ;cout<<"4\n" ;
[Link](300) ;cout<<"3\n" ;}
catch(BigNum big){[Link]();}
catch(stack :: empty){} }}
C++
Horizon
257
Exercises
Create the following classes :
Corresponding to every user there exists a physical file in the disk
which has the same name as the user account no ( eg, u01 for acc no
1). This file contains the balance in savings account and the total
amount in fixed deposit and the no of fixed deposits.
There is a second file per user (eg, fd01 for
acc no 1). This file contains the period,
amount and fdNo for each fd for that
user.
Account
Name
AccountNo
Savings
FixedDeposit
Balance
Period
Amount
There is a master file (MASTER)
containing the user name and the account
no sorted on account nos.
Write program through which a new
account can be created, an existing one
deleted or accessed for viewing.
C++
Horizon
258
The program handles 10 maximum [Link] get incremented
initially till 10 and then the unused ones are reused lying between 1 to
10.
When an existing account is deleted, its user name is marked as
XXXX across the name.
Throw exception if user tries to withdraw more than (balance-1000)
for savings or (more than amt / before period) for fixed deposits.
A user can have any number of fixed deposits.
C++
Horizon
259
Write a function that searches a binary tree of nodes based on a
char * field for a match. If a node containing hello is found,
find(hello) will return a pointer to that [Link] exception to
indicate not found.
Define a class Int that acts exactly like the built-in type int except
that it throws exceptions rather than overflow or underflow.
C++
Horizon
260
Dynamic Polymorphism And
VTABLE
C++
Horizon
261
Dynamic Polymorphism
Dynamic polymorphism is achieved by the use of Virtual
Functions.
How is dynamic polymorphism achieved ?
Pointer to base class is used to refer to a derived class object.
The function with the same name (say test) in the base and the
derived class is preceded by the keyword virtual in the base
class.
When a base class pointer is referring to a derived class object
then if the function test is invoked using such a pointer then the
derived class version is executed.
If in the base class this function is not preceded by the keyword
virtual then it is the base class version of the function is invoked.
C++
Horizon
262
Example :
class A {
public : virtual void f( ) { cout<<ff in AAAA\n ; }
void p( ) { cout<<pp in AAAA\n ;}
};
class B : public A {
public : void f( ) { cout<<ff in BBBB\n ; }
void p( ) { cout<<pp in BBBB\n ;}
};
main( )
{ B derived_obj ; A base_obj ;
A * base_ptr = & base_obj ;
base_ptr->f( ) ; base_ptr->p( ) ; // base versions of f( ) & p( ) called
base_ptr = & derived_obj ;
base_ptr->f( ) ; // derived versions of f( ) called
base_ptr->p( ) ; // base versions of p( ) called
}
C++
Horizon
263
Example :
class base {
public : virtual void f() {cout<<"base\n" ;} } ;
class der_b : public base {
public : void f() {cout<<"der1\n" ;} } ;
class der_d : public der_b {
public : void f() {cout<<"der2\n" ;} } ;
void main() {
base *bptr ; der_b dObj ; der_d ddObj ;
bptr = &dObj ; bptr->f() ; //der 1
bptr = &ddObj ; bptr->f() ;
//der 2
der_b *dptr = &ddObj ;
dptr->f() ;
//der 2
}
C++
Horizon
264
How use of virtual functions results in dynamic polymorphism?
In case the base class pointer is pointing to an object of a derived
class and a virtual function of the base class is invoked through
such a pointer then :
the compiler selects the function to be called based on the
contents of the pointer and not on the type of the pointer
but the compiler does not know the contents of ptr because ptr
gets initialized to the derived address only at run time
so it is not known at compile time the class to which the
function belongs
so the decision of which version of the function is to be called,
is deferred till the program is running.
C++
Horizon
265
Hence it is also known as late binding.
Even references can be used to get the same results.
C++
Horizon
266
Rules for Virtual Functions
The virtual functions must be members of some class.
They cannot be static members
A virtual function can be a friend of another class.
A virtual function in a base class must be defined, even though
it may not be used.
The prototypes of the base class version of a virtual function
and all the derived class versions must be identical.
Virtual constructors are not possible but virtual destructors are
possible.
C++
Horizon
267
A base class pointer can point to a derived class object but the
reverse is not true.
When a base pointer points to a derived class, incrementing or
decrementing it does not make it point to the next object of the
derived class. It is incremented or decremented only relative to
its base type.
It is not necessary to redefine a virtual function in the derived
classes. If not redefined in the derived classes the base version
is invoked.
C++
Horizon
268
Pure Virtual Functions
Pure virtual functions are defined as follows :
virtual return_type function_name(args_list) = 0 ;
A pure virtual function is declared in a base class if the
function has no definition relative to the base class.
If there exists a pure virtual function in a base class then it is
compulsory that each derived class does one of the following
things :
either the function is re-declared a pure virtual function
in the derived class
or the derived class provides a definition for the pure
virtual function.
A class containing a pure virtual function is an abstract class
and hence can not be used to create objects.
C++
Horizon
269
Abstract Classes
Abstract classes are those that can not be used for creating
objects.
To ensure that such classes are not used for object creation
the functions of the class can be declared as pure virtual.
The main objective of abstract classes is to provide some
common traits to the derived classes and to create a base
pointer required for achieving runtime polymorphism.
A class is abstract if it has at least one pure virtual function.
An abstract class may not be used as an argument type, as a
function return type, or as the type of an explicit conversion.
Pointers and references to an abstract class can be declared.
C++
Horizon
270
VTABLES
C++
Horizon
271
class A {
int i ;
public : virtual void f() {cout<<"AAA\n" ;}
};
class B {
int i ;
public : void f() {cout<<"BBBB\n" ;}
};
class C {
public : void f() {cout<<"CCCC\n" ;}
};
class D { } ;
main() {
A a;B b;C c;D d;
cout<<sizeof(A)<<endl<<sizeof(B)<<endl<<sizeof(C)<<endl<<
sizeof(D)<<endl ;
}
C++
Horizon
272
The size of class B is 4 : size of int data type.
The size of class A is 8 : (size of int) + (size of a void pointer)
The sizes of class C and D are 1 : so that objects of these classes
have non-zero sizes. (There can not be an array of zero sized
objects)
C++
Horizon
273
With virtual functions in a class the following is true :
The compiler creates a VTABLE for each class that contains
virtual functions and for the classes derived from it.
This VTABLE of a class contains addresses of the virtual
functions for that particular class.
If a derived class does not redefine a virtual function of the base
class then the compiler places the address of the base class version
itself in the VTABLE of the derived class.
The compiler adds a void pointer to an object of such base and
derived classes containing virtual functions.
This pointer, called the vpointer (VPTR) points to the classs
VTABLE.
C++
Horizon
274
Whenever a virtual function is invoked using the base class
pointer, the compiler inserts the code to fetch the VPTR and to
look up the function address in the VTABLE.
This causes late binding to take place to invoke the correct
virtual function because the VPTR gets initialized at run-time.
When a virtual function is invoked on a base class pointer
containing the address of a derived class object then :
The compiler reads the contents of the base class pointer.
Through this the compiler reads the derived objects VPTR.
Through this VPTR the derived class VTABLE is accessed.
From this table address of function being invoked is fetched.
Using this address, function of derived class is accessed.
C++
Horizon
275
Other related issues :
It is important that no call to a virtual function is made before
the VPTR is initialized to point to the correct VTABLE.
The VPTR is automatically initialized in the constructor.
This is why the compiler adds a zero-argument constructor if
not provided in a class to initialize the VPTR if required.
All objects have their VPTR in the same place usually in the
beginning of the object. This makes extraction of VPTR easy.
C++
Horizon
276
class A {
public :
virtual void f( ) { cout <<f :: AAAA\n ; }
};
class B : public A {
public :
void f( ) { cout <<f :: BBBB\n ; }
virtual void g( ) { cout <<g :: BBBB\n ; }
};
main( )
{
B derived_obj ;
A * base_ptr = & derived_obj ;
base_ptr -> g( ) ;
//compilation error
}
C++
Horizon
277
This code generates a compilation error because g( ) is not a
member of class A.
Though the VTABLE contains both the functions f( ) and g( ), but
the compiler does not know what objects address is available in
the base_ptr.
So the compiler decision whether the member invoked is correct or
not is based on the type of base_ptr.
Once the code compiles successfully then only the VTABLES
come into picture for the virtual functions.
C++
base VTABLE
derived VTABLE
&base :: f( )
&derived :: f( )
&derived :: g( )
Horizon
278
If the type of the object pointed by the base pointer is known
during code generation then the following can be used :
((B *) base_ptr) -> g( ) ; //base address casted to derived address
Casting the derived address to base address is always safe.
The reverse is not true.
C++
Horizon
279
Object Slicing
class A {
public : virtual void f( ) { cout<<"AAAA\n" ;}
};
class B : public A {
public : void f( ) { cout<<"BBBB\n" ;}
};
main( ) {
A a;B
a=b;
a.f( ) ;
}
b;
Object b is sliced so that it becomes the smaller
object equivalent to a type copying only the
base portion of the object.
This code invokes the base version of f( ).
C++
Horizon
280
Invoking Virtual Functions From
Constructors/Destructors
Virtual functions can
constructor/destructor.
be
invoked
from
within
In this case it is the member function of the current class that gets
invoked, i.e. the virtual mechanism doesnt work and no
polymorphism is achieved.
see next slide for eg
C++
Horizon
281
class b {
public :
b() {cout<<"invoking f from base constr ::: " ;f() ;}
virtual void f() {cout <<"base\n" ;}
void g() {cout<<"invoking f from g ::: " ;f() ;}
};
class d : public b { int I ;
public :
d() {I = 90 ;cout<<"invoking f from der constr ::: " ;f() ;}
void f() {cout<<"der\n" ; I = 88 ;}
};
void main() {
b * p = new b ;
p->g() ;
cout<<"1-----\n" ;
p = new d ;
p->g() ;
}
C++
OUTPUT
invoking f from base constr ::: base
invoking f from g ::: base
1---invoking f from base constr ::: base
invoking f from der constr ::: der
invoking f from g ::: der
Horizon
282
Virtual Destructors
class A {
public :
A( ){cout<<"base cons\n" ;}
~A( ){cout<<"base destr\n" ;}
};
class B : public A {
public :
B( ){cout<<"der cons\n" ;}
~B( ){cout<<"der destr\n" ;}
};
main( ) {
A * ptr = new B ;
delete(ptr) ;
}
C++
Horizon
Output :
base cons
der cons
base destr
283
The object is not destroyed fully as the derived class destructor
is never invoked.
If we can force the invocation of the derived destructor then the
base destructor is anyway automatically called.
This can be done by declaring the base destructor as virtual.
Virtual destructors are possible because :
During destruction the type of the object is already known and
the VPTR already initialized.
This is not the case with the constructor.
C++
Horizon
284
Pure virtual destructors can be created but they must be
provided with the body.
This is because all destructors in a class hierarchy get invoked
during destruction of an object.
Thus in the previous code if the base destructor is preceded by
the keyword virtual then the derived destructor is also invoked.
The output would be as follows :
base cons
der cons
der destr
base destr
C++
Horizon
285
Dependencies within a Class
Hierarchy
Consider the following code :
#include <iostream.h>
class base {
public :
virtual void f() {cout<<"f of base\n" ;}
void g() {f() ;} } ;
class der1 : public base {
public :
void f() {cout<<"f of der1\n" ;} } ;
class der2 : public base {
public :
void f() {cout<<"f of der2\n" ;}} ;
void main() {
base * ptr = new base ; ptr->g() ;
ptr = new der1 ; ptr->g() ;
ptr = new der2 ; ptr->g() ; }
C++
Horizon
Output :
f of base
f of der1
f of der2
286
Thus if the member function of a base class itself calls one of the
classs virtual functions, then the base class depends on its derived
classes for its own implementation.
The requirement for such a code would be visible in the following
example.
Consider the following integer buffer class :
class buffer {
// . . .
void put(int) ;
int get() ;} ;
The overflow and the underflow implementation could be
hardwired into the class, but such a code would have limited
usefulness.
If the put() and get() functions call virtual functions overflow()
and underflow() respectively, different types of buffers can be
tailored to suit a variety of needs.
The following piece of code depicts this.
C++
Horizon
287
class buffer {
// . . .
virtual int overflow(int) ;
virtual int underflow() ;
void put(int) ; // call overflow(int) if buffer is full
int get() ;} ; // call underflow(int) if buffer is empty
class circular_buffer : public buffer {
// . . .
int overflow(int) ; // wrap around when buffer is full
int underflow() ; } ;
class expanding_buffer : public buffer {
// . . .
virtual int overflow(int) ; // increase buffer size when full
virtual int underflow() ; } ;
C++
Horizon
288
Run-time Type Information
RTTI is a mechanism that allows the type of an object in an
inheritance hierarchy to be determined during program
execution.
RTTI was added to the C++ language because many vendors
of class libraries were implementing this functionality
themselves. This caused incompatibilities between libraries.
Thus, it became obvious that support for run-time type
information was needed at the language level.
The concept applies to pointers as well as references.
C++
Horizon
289
THE NEED FOR RTTI: ( a simple example )
class shape { } ;
class circle : public shape { } ;
class rectangle : public shape { } ;
main( )
{
shape * sh [5] ;
//some elements of sh point to circle object
//the remaining point to rectangle object
}
If we need to change the colour of all the circles only then we need to
downcast the base pointer back to that of the correct derived class.
C++
Horizon
290
There are three main C++ language elements to run-time type information:
The typeid operator - used for identifying the exact type of an object
This operator returns the run-time type of an object. If the operand provided
to the typeid operator is the name of a type, the operator returns an object that
identifies it. If the operand provided is an expression, typeid returns the type
of the object that the expression denotes.
The dynamic_cast operator - used for conversion of polymorphic types.
This operator combines type-checking and casting in one operation. It checks
whether the requested cast is valid, and performs the cast only if it is valid.
The type_info class - used to hold the type information returned by the typeid
operator.
This class describes the RTTI available, and is used to define the type returned
by the typeid operator. This class provides to users the possibility of shaping
and extending RTTI to suit their own needs. This ability is of most interest to
implementers of object I/O systems such as debuggers or database systems.
C++
Horizon
291
usage of typeid
- a simple example
class shape {} ;
class circle : public shape {} ; // ALL THE MEMBERS OF THE THREE CLASSES
class rectangle : public shape {} ; // ARE NON VIRTUAL
void main ( ) {
shape *sh[8] ; int i ;
for (i = 0; i < 8; i++) {
if (i % 2 == 0)
sh[i] = new circle() ;
else
sh[i] = new rectangle() ;
}
for (i = 0; i < 8; i++)
cout<<endl<<typeid(sh[i]).name()<<" :: "<<typeid(*sh[i]).name() ;
}
OUTPUT:
class shape * : : class shape
class shape * : : class shape
class shape * : : class shape
C++
total 8 times
Horizon
292
usage of typeid
contd
include typeinfo.h
class shape {public : virtual void ar ( ){}} ;
class circle : public shape {public : void ar ( ){}} ;
class rectangle : public shape {public : void ar ( ){}} ;
void main ( ) {
shape *sh[8] ; int i ;
for (i = 0; i < 8; i++) {
if (i % 2 == 0)
sh[i] = new circle() ;
else
sh[i] = new rectangle() ;
}
for (i = 0; i < 8; i++)
cout<<endl<<typeid(sh[i]).name()<<" :: "<<typeid(*sh[i]).name() ;
}
OUTPUT:
class shape * : : class circle
class shape * : : class rectangle
class shape * : : class circle and so on
C++
Horizon
293
The example emphasizes the following :
RTTI should be used only with polymorphic classes i.e. those
which have a virtual function in the base class. (as in the second
example)
In the absence of polymorphism the static type information is
used. (as in the first example)
typeid ( ) can be used with built-in types also.
cout<<endl<<typeid(45).name() ; //output -: int
C++
Horizon
294
usage of dynamic_cast
class shape {public : virtual void ar ( ){}} ;
class circle : public shape {public : void ar ( ){}} ;
class rectangle : public shape {public : void ar ( ){}} ;
void main ( ) {
shape *sh[8] ; int i ;
for (i = 0; i < 8; i++) {
if (i % 2 == 0) sh[i] = new circle() ;
else
sh[i] = new rectangle() ;
}
circle * cir_ptr ;
for (i = 0; i < 8; i++)
{
if(cir_ptr = dynamic_cast<circle *>(sh[i])) // dynamic_cast successful
cout<<endl<<"dynamic_cast :: circle" ;
else
// dynamic-cast unsuccessful
cout<<endl<<"dynamic_cast :: rectangle" ;
}
}
C++
Horizon
295
another example of dynamic_cast (complete the code and check)
class employee {
public:
virtual int salary();
};
NOTE :
In the above program, dynamic casts
are needed only if the base class
employee and its derived classes are
not available to users (as in part of a
class manager : public employee {
library where it is undesirable to
public:
modify the source code). Otherwise,
int salary();
adding new virtual functions and
virtual int bonus();
providing derived classes with
};
specialized definitions for those
functions is a better way to solve
void calc (employee *pe) {
this problem.
// employee salary calculation
if (manager *pm = dynamic_cast<manager*>(pe)) {
// use manager::bonus()
}
else {
// use employee's member functions
}
C++
Horizon
296
Upcasting with dynamic_cast
Means moving a pointer up a class
heirarchy.
class grandP {public : virtual void testF() {cout<<"grandP f\n" ;}} ;
class parent : public grandP {
public : void parF( ) {}
virtual void testF( ) {cout<<"parent f\n" ;}} ;
class child : public parent {
public : void testF( ) {cout<<"child f\n" ;}} ;
main( ){
grandP * ptr ; parent par ;
ptr = &par ; ptr->testF() ;//parent ver invoked
//dynamic polymorphism
grandP g = *ptr ;
//object slicing
[Link]() ; //grandP version invoked
grandP *gPtrTemp = dynamic_cast<grandP *>(ptr) ;
gPtrTemp->testF() ; //parent
parent *pPtrTemp = dynamic_cast<parent *>(ptr) ;
pPtrTemp->testF() ; //parent
pPtrTemp->parF() ;
child
chd ;
gPtrTemp = dynamic_cast<grandP *>(&chd) ;
gPtrTemp->testF() ;} //child
C++
Horizon
297
In this example :
gPtrTemp->parF() can not be invoked because:
gPtrTemp is now casted to the object of the base class.
Thus parF() function is not a member.
But the virtual function is still of the derived class only bec
it is accessed thru the vptr and the vtable
these get initialized at the time the object is created.
This type of conversion is called an upcast
It moves a pointer up a class hierarchy, from a derived class to a class
it is derived from.
An upcast is an implicit conversion.
C++
Horizon
298
While downcasting i.e., moving a pointer down the class heirarchy,
care must be taken that the casting is valid.
Following is erroneous :
class base { public : virtual void f(){cout<<"base\n";}} ;
class der1:public base { public :
void f() {cout<<"der1\n";}} ;
class der2:public base {
public : void f() {cout<<"der2\n";}
void g() {cout<<"g in der2\n" ;}} ;
main() {
base * bPtr = new der1() ;
der1 * d1Ptr = dynamic_cast<der1 *>(bPtr) ;
d1Ptr->f() ; //conversion allowed
der2 * d2Ptr = dynamic_cast<der2 *>(bPtr) ; // error
base b ;
d1Ptr = dynamic_cast<der1 *>(&b) ; //error
}
C++
Horizon
299
Other related operators :
static_cast (resolve at compile time)
reinterpret_cast
const_cast
C++
Horizon
300
Exercises
Create the following classes :
a. shape (the base class)
b. line, rectangle and myshape (derived from shape)
myshape draws a face as shown below :
Ultimately the program should be designed to draw the following :
You may use some graphics / screen functions for actual
implementations.
C++
Horizon
301
Develop
the functions writeToDisc(const Persistent &) and
readFromDisc(Persistent &).Create a class Persistent that contains
virtual functions save( ) and read( ) [define the interfaces for these
functions as required]. These member functions of Persistent class
would be invoked by the above functions.
Any class that needs to be written to / read from the disc provides its
own implementation of these functions.
Test this code for any two of your own classes.
Create the following classes with some basic functionalities
identified, and identify the relationships between the classes :
a. window
Develop a class based on these
b. scrollbar
classes that would generate the
c. Vert_scrollbar
following window :
d. Hori_scrollbar
e. window_with_scrollbars
f. Button
C++
Horizon
302
Interfaces
An interface :
defines a set of operations
the operation implementations may be missing
these implementations are provided by the classes
implementing the interfaces.
supports the inheritance features
these features are with respect to other interfaces only
provides the objects identity
C++
Horizon
303
Interfaces in C++ :
Interfaces are implemented in C++ as abstract base classes.
All methods are declared as pure virtual functions.
This abstract class thus can not be used for creating objects.
This interface (abstract class) is then inherited by the class that
wants to implement the interface.
The inherited class then has the responsibility to provide
implementations for the operations given in the interface.
C++
Horizon
304
An ideal interface :
presents a complete and coherent set of concepts to a user
is consistent over all parts of a component
does not reveal implementation details to a user
can be implemented in several ways
depends in limited and well-defined ways on other interfaces.
C++
Horizon
305
Namespaces
Namespace provides a scope to the visibility of a name for a
class, function, structure, union, variable etc.
C++ provides a single global namespace by default.
We can split this single global namespace into multiple namespaces
by using the keyword namespace.
Declarations that fall outside all namespaces remain members of
the global namespace.
A namespace definition can appear only at a global scope
void main( ) { namespace local { } }
This would generate an error.
C++
Horizon
306
namespace space1 {
class big {
public :
void f() {cout<<"space1\n" ;} } ;
}
namespace space2 {
class big {
public :
void f() {cout<<"space2\n" ;} } ;
}
void main()
{
space1 ::big b1 ;
b1.f() ;
space2 ::big b2 ;
b2.f() ;
}
C++
Horizon
307
namespace space1 {
class big {
public :
void f() {cout<<"space1\n" ;} } ;
}
namespace space2 {
class big {
public :
void f() {cout<<"space2\n" ;} } ;
}
void main()
{
{
using namespace space1 ;
big b1 ;
b1.f() ;
}
using namespace space2 ;
big b2 ;
b2.f() ;
C++
}
The curly braces specify the scope for
using namespace space1.
Otherwise after using namespace space2
both namespaces are used.
Horizon
308
#include <iostream.h>
void f() { cout<<"global f\n" ; }
void g() { cout<<"global g\n" ; }
namespace space1 {
namespace space2 { void g() {cout<<"sp1 :: sp2 :: g\n" ;} }
void f() {cout<<"function fun\n" ;}
class big { public : void f() {cout<<"space1\n" ;} } ;
}
namespace space2 {
class big { public : void f() {cout<<"space2\n" ;} } ;
}
void main() {
using namespace space1 ;
big b1 ;
b1.f() ;
space1::f() ; // Since this function is also present in the global namespace, the
// specific function has to be identified using scope resolution. Else
//
it generates an error.
::f() ; // Global f() executed.
//using namespace space1 :: space2 ;// This statement has no effect.
space1::space2::g() ;
C++
Horizon
309
}
A namespace definition can be continued over multiple header
files. Example :
// mylib1.h
namespace sp1 { void f( ) ; class c { } ;}
// mylib2.h
namespace sp1 { void g( ) ; class d { } ;}
The continuation of namespace after the initial definition is called
extension-namespace-definition.
Members of a namespace can be defined within (as shown in the
previous examples) or outside that namespace ( as shown below)
namespace space1
namespace space1 {
void f() ; // defined outside the namespace
class big {
public :
void f() {cout<<"space1\n" ;} } ; }
It is necessary that
the definition follows
the declaration.
void space1 :: f() {cout<<"function fun\n" ;}
C++
Horizon
310
A namespace can be given an alternate name (to prevent typing
long names) :
namespace space1 {
void f() ; }
namespace sp1=space1 ;
void sp1 :: f() {cout<<"function fun\n" ;}
A global namespace cannot be the same as any other global entity
name in a particular program.
C++
Horizon
311
Memory Management
C++
Horizon
312
C++ Memory Use
C++ program variables and objects may occupy memory that is
obtained in three different ways :
Static memory refers to memory allocated during compilation
examples are global data and static data
these exist when program is loaded until end of run
Automatic
memory
local variables and objects in each function or block
created on the stack on entry
destroyed (destructed) on exit
Dynamic
memory
(Free Store)
obtained from operating system, using new
allocated from the system free store
objects exist until end of run or delete
C++
Horizon
313
Static Store
class table {
public :
table(int i) {cout<<"table constr : "<<i<<"\n" ;} } ;
table tbl_1(20) ;
void f(int i) { static table tbl_2(i) ; }
Output : table constr : 20
main( ) {
main 1
cout<<"main 1\n" ;
table constr : 30
f (30) ; f(10) ;
main 2
cout<<"main 2\n" ; }
Here the constructor table :: table(int) is executed twice -: once
each for tbl_1 and tbl_2.
The destructor is also executed twice -: to destroy tbl_1 and
tbl_2 after exit from main( ).
Constructors for global static objects (eg : tbl_1) are executed in
an order in which the declarations occur. Destructors are called
in reverse order.
Constructors for local static objects (eg : tbl_2) are executed the
first time the thread of control passes through the objects
definition.
C++
Horizon
314
By allocating a global static object for a class with constructor
and/or destructor a programmer has a simple way of specifying
code to be executed before and/or after the call to main( ).
If a program is terminated using exit function the destructors for
constructed static objects are called. Thus exit( ) does not
terminate the program immediately.
If a program is terminated using abort function the destructors
for constructed static objects are not called.
C++
Horizon
315
#include <iostream.h>
class table {
public : table(int i) {cout<<"constr\n" ;}
~table() {cout<<"destr\n" ;}
};
table tbl_1(50) ;
void f(int i)
{
static table tbl_2(i) ;
}
main( )
{
cout<<"in main\n" ;
f (30) ;
f(20) ;
cout<<"programme terminates\n" ;
}
C++
OUTPUT
constr
in main
constr
programme terminates
destr
destr
Horizon
316
The C++ Free Storage
All dynamic memory requests are satisfied through an area of
memory called heap. These requests are made through the new
operator.
It is the responsibility of the programmer to return dynamically
allocated memory to the free pool, by using the delete operator.
new/delete operations take a lot of CPU time.
Ideally to every new there should be a matching delete.
C++
Horizon
317
new AND delete
In addition to allocating memory new also creates an object by
calling the objects constructor.
In addition to deallocating memory delete also destroys the
object by calling the objects destructor.
While allocating memory using new it is allocated from the free
store (the heap).
An object created using new exists until it is explicitly destroyed
using delete.
The expression delete p doesnt delete the pointer rather the
object pointed to by p (i.e., releases the memory held by p).
C++
Horizon
318
class jlt { int
i;
public : void f() {cout<<"\nfunc in jlt" ;} } ;
void main() {
jlt *p = new jlt ;
cout<<"\ndefore delete, p :: "<<p ;
delete(p) ;
cout<<"\nafter delete, p :: "<<p ;
}
Thus even after delete(p) is executed p still contains the same
address. Hence p still points to the same memory location which
should not be now accessed with the same meaning.
Worst is to re delete(p).
C++
Horizon
319
Taking Over Memory Management for a class by re-defining
new() and delete() operators.
The new and delete operators are used to allocate and deallocate
free store, respectively.
One can define own versions of new and delete for a class by
overloading them.
When new and delete operate on class objects, the class member
operator functions new and delete are called, if they have been
declared.
C++
Horizon
320
If a class object is created with the new operator, one of the
operator functions operator new() or operator new[]() (if they have
been declared) is called to create the object.
An operator new() or operator new[]() for a class is always a static
class member, even if it is not declared with the keyword static. It
has a return type void* and its first parameter must be the size of the
object type and have type std::size_t. It cannot be virtual.
If a class object is destroyed with the delete operator, the operator
function operator delete() or operator delete[]() (if they have been
declared) is called to destroy the object.
An operator delete() or operator delete[]() for a class is always a
static member, even if it is not declared with the keyword static.
Their first parameter must have type void* and have a return type
void. It cannot be virtual.
C++
Horizon
321
The delete operator destroys an object created by the new operator.
If delete is called for an object with a destructor, the destructor is
invoked before the object is deallocated.
If new and delete are called for a class object that does not declare
the operator functions new and delete, or they are called for a
nonclass object, the global operators new and delete are used.
The C++ operators for allocating and deallocating arrays of class
objects are operator new[ ]() and operator delete[ ]().
C++
Horizon
322
#include <iostream> #include <malloc.h> using namespace std ;
class MyClass { int mem ;
public : MyClass() { cout << "obj constr\n" ;}
void * operator new(size_t s) {cout<<"my new, size is :"<<s<<"\n" ;return malloc(s) ;}
void * operator new [](size_t s) {
OutPut :
cout<<"my new for arr, size is :"<<s<<"\n" ;
obj constr
return malloc(s) ; }
my new, size is :4
void operator delete(void * p) { cout<<"my delete\n" ;
free(p) ; }
obj constr
void operator delete [](void * p, size_t t) {
trying array
cout<<"my delete for arr, size is :"<<t<<"\n" ;
my new for arr, size is :32
free(p) ; }
obj constr
~MyClass() { cout << "obj destr\n" ;}
obj constr
};
obj constr
obj constr
void main() {
trying delete
MyClass
obj1 ;
obj destr
MyClass
* p1 = new MyClass ;
my delete
cout << "\ntrying array ...\n" ;
obj destr
MyClass
* p2 = new MyClass[7] ;
obj destr
cout<<"\ntrying delete ...\n" ;
obj destr
delete p1 ;
obj destr
delete[] p2 ;
my delete for arr, size is :4
}
obj destr
C++
Horizon
323
Thus new and delete can be summarized as follows:
new
-obtains memory from the system free store
-the object is then initialized
-the constructor is run
-returns a pointer to the dynamically created object
delete
-the destructor is run
-releases the memory to the operating system
C++
Horizon
324
Exercises
Write a template class to represent complex numbers of integers,
or doubles. Overload the +, - and * operators to handle the
functionalities for the template complex class.
Write a template class stack that provides for its own memory
management routines.
Define
the
classes
buffer,
circular_buffer
and
expanding_buffer as mentioned in slide#279. Implement them as
template classes and implement the overflow and underflow
conditions accordingly.
C++
Horizon
325
Standard Template Library
provides a library of reusable
components
STL is a C++ programming library that has been designed
to enable a C++ programmer to do generic programming
and is based on the extensive use of templates - also called
parameterized types.
C++
Horizon
326
Basic Terms :
CONTAINER CLASS :
a class that can contain objects, example : lists, sets, arrays
it provides a way to organize data in memory
the type of objects contained are of little interest to the definer of
the container class i.e., the type of the object contained itself is
an argument to the container class.
ALGORITHMS :
define computational procedures.
a broad range of fundamental algorithms including sorting,
searching, and transforming are defined.
they can be used with any container with sufficient functionality,
for example - random access instead of linear access.
C++
Horizon
327
ITERATORS :
provide a means of stepping through the items contained by a
container.
FUNCTIONS :
can be encapsulated inside an object for use by other
components.
ADAPTORS :
adapt a component to present a different interface - to define a
reverse iterator, or present a stack-like interface, for example.
C++
Horizon
328
The Standard Template Library provides a set of parameterized
container classes and other components, to which algorithms can
be applied in a generic way.
The library is structured so that any algorithm will work with
any container providing it meets certain specifications.
The STL contains five main categories of generic components.
C++
Horizon
329
How Components Work Together
Containers store objects of arbitrary types.
Containers are parametrized by allocators.
Allocators are objects which encapsulate information about the
memory model used. They provide memory primitives to handle
memory accesses uniformly.
Containers use allocators to do their memory accesses. A change
of the memory model used leads to a change of the allocator
object given as an argument to the container. This means, that on
the code level a container object is invariant under different
memory models.
An algorithm is a computation order. So, two algorithms should
differ in the computations done by them, not in the access method
used to read input data and write output data.
C++
Horizon
330
STL provides a uniform data access mechanism for its algorithms
- iterators. Different iterators provide different access modes.
Function objects are used in combination with algorithms to
encapsulate functions and operations to extend the algorithms'
utility.
Adaptors are interface mappings, they implement new objects
with different or enhanced functionality on the basis of existing
components.
C++
Horizon
331
Overview of STL components
Containers
Sequence containers
Sorted Associative Containers
Generic Algorithms
find, merge, search, copy, count, sort, accumulate
Iterators
Function Objects
Adaptors
Allocators
C++
Horizon
332
Containers
Containers are data structures that manage a collection of
elements and are responsible for the allocation and de-allocation
of those elements.
Containers typically have constructors and destructors along
with operations for inserting and deleting elements.
STL containers contain a minimal set of operations for creating,
copying, and destroying the container along with operations for
adding and removing elements.
There are no member functions for examining the elements in a
container or sorting them. Instead, algorithms have been
decoupled from the container and can only interact with a
container via traversal by an iterator.
C++
Horizon
333
The STL provides two categories of containers:
Sequence containers
store elements in sequential order
group a finite set of elements in a linear arrangement.
all sequence containers provide iterators
the STL includes class templates for vectors, lists, deques
Associative containers
store elements based on a key value
provide efficient retrieval of elements based on their key
the collection is maintained in order, based on a comparison
function object of type Compare
container size can vary at run-time, and their contents are
maintained in a sorted order
support functions such as insertion of elements and ranges of
elements, unique insertion, search by key and erasure of
elements or ranges
the STL provides class templates for maps, multimaps, sets,
and multisets.
C++
Horizon
334
vector :
contains a block of contiguous initialized elements
defines a dynamic array - an array that can grow during use (the
vector automatically resizes)
provides array-like fast random access
supports insertion and deletion at any point in array, this being a
linear time operation
provides constant time insertions and deletions at the end
supports the standard array-indexing syntax for accessing its
elements by overloading [ ] operator (random access iterator)
Thus a vector object can be indexed like an array and an element can
be accessed using the subscript notation.
C++
Horizon
335
Although vectors allow array-style indexing, their elements can be
also accessed through an iterator. The iterator is defined by
container classes.
To obtain an iterator for vector : vector <T>::iterator i
The template specification for vector:
template <class T, class Allocator=allocator <T> > class vector
where T is the type of data being stored and Allocator specifies the
allocator which defaults to the standard allocator
C++
Horizon
336
Constructors :
vector (const Allocator &a=Allocator() ) Constructs an empty vector
vector (size_type num, const T &val=T() , const Allocator &a=Allocator() )
Constructs a vector that has num elements with value val
vector (const vector <T, Allocator> &ob) vectors copy constructor.
template <class InIter>
vector (InIter start, InIter end, const Allocator &a=Allocator() )
Constructs a vector that contains the elements in the range specified by start and
end.
C++
Horizon
337
Some of the most commonly used member functions are:
iterator begin ()
iterator end ()
bool empty ()
Returns an iterator to the first element in the vector.
Returns an iterator to the end of the vector.
Returns true if the vector contains no elements.
iterator erase (iterator i) Removes the element pointed to by i. Returns an iterator to the
element after the one removed.
iterator erase (iterator start, iterator end) Removes the elements in range start to end. Returns
an iterator to the element after the one removed.
template <class InIter> void insert (iterator i, InIter start, InIter end) Inserts the sequence
defined by start and end immediately before the
element specified by i.
void pop_back ()
Removes the last element in the vector.
void push_back (const T &val) Adds an element with the value specified by val to the end of
the vector.
size_type size ()
Returns the number of elements currently in the
vector.
To use vector class include <vector>.
C++
Horizon
338
Deletion of the first element can be easily programmed on vector using
method [Link]([Link]()), but that kind of operation should be avoided
because it is very inefficient for long vectors since all of the elements
after the first position must be shifted over to fill the hole, it is a linear
time operation.
C++
Horizon
339
#include <vector> #include <string> #include <algorithm>
#include <assert.h> #include <iostream> using namespace std ;
template<class T>void print(vector<T> v) {
for(int i = 0; i < [Link](); i++)
cout<<v[i]<<"\t";
cout<<endl; }
main() {
vector<int> vi; vector<string> vs;
for(int i = 0; i < 20; i++) vi.push_back(i);
assert(![Link]()); print(vi);
cout<<"Changing values of elements in range 3 through 7...\n";
for(i = 3; i < 8; i++) vi[i] = 100; print(vi);
cout<<"Removing elements\n"; [Link](&vi[3], &vi[8]); print(vi);
vs.push_back("John"); vs.push_back("Tom"); vs.push_back("Jane");
vs.push_back("Anna");
print(vs);
cout<<"\nSorting elements...\n";
sort([Link](), [Link]()); print(vs);
cout<<"\nRemoving last element...\n";
C++
vs.pop_back();
Horizon
print(vs); }
340
lists :
supports bidirectional access, most often implemented as
doubly-linked list and may be traversed in either direction
insert and erase operations anywhere within the sequence take
place in constant time
manages allocation and deallocation automatically
fast random access is not supported provides linear time access
some generic algorithms require random access, hence these
operations are provided as member functions of the list class
C++
Horizon
341
The list class supports bi-directional iterator. Thus, the container
can be accessed through an iterator in both forward and reverse
direction.
To obtain an iterator for list : list <T>::iterator i
The template specification is :
template <class T, class Allocator=allocator <T> > class list
where T is the type of data being stored and Allocator specifies the allocator
which defaults to the standard allocator.
C++
Horizon
342
Constructors:
list (const Allocator &a=Allocator() ) Constructs an empty list
list (size_type num, const T &val=T(),const Allocator &a=Allocator() )
Constructs a list that has num elements with value val
list (const list <T, Allocator> &ob)
list's copy constructor.
template <class InIter>
list (InIter start, InIter end, const Allocator &a=Allocator() )
Constructs a list that contains the elements in the range specified by start and
end.
.
C++
Horizon
343
Some of the most commonly used member functions are:
iterator begin ()
iterator end ()
bool empty ()
reference back ()
reference front ()
Returns an iterator to the first element in the list.
Returns an iterator to the end of the list.
Returns true if the list contains no elements.
Returns a reference to the last element in the list
Returns a reference to the first element in the list
iterator erase (iterator i)
iterator erase (iterator start, iterator end)
iterator insert (iterator i, const T &val)
void insert (iterator i, size_type num, const T &val)
template <class InIter> void insert (iterator i, InIter start, InIter end)
void pop_back ()
Removes the last element in the list.
void pop_front ()
Removes the first element in the list
void push_back (const T &val)
void push_front (const T &val) Adds an element with value specified by val to front of list.
void remove (const T &val) Removes elements with the value val from the list
size_type size ()
Returns the number of elements currently in the
vector.
C++
Horizon
344
Some special member functions are:
void merge (list<T, Allocator>&ob)
void sort ()
Merges ordered list contained in ob with the
ordered invoking list. The result is ordered. After
the merge , the list contained in ob is empty
Sorts the list.
void splice (iterator i, list <T, Allocator> &ob) The contents of ob are inserted into the
invoking list at the location pointed by i. After the
splice, ob is empty
void splice (iterator i, list <T, Allocator> &ob, iterator start, iterator end)
The range defined by start and end is removed from ob and stred in the invoking list
beginning at the location pointed to by i.
C++
Horizon
345
#include <list> #include <string> #include <assert.h>
#include <iostream> using namespace std;
template<class T> void print(list<T> l) {
list<T>::iterator it = [Link](); //obtaining iterator
while(it != [Link]()) cout<<*it++<<"\t"; cout<<endl; }
main() { list<int> li; list<string> ls;
for(int i = 0; i < 20; i++) li.push_back(i);
print(li);
cout<<"Removing first 5 elements...\n";
for(i=0; i<5; i++) {
cout<<[Link]()<<" removed\n"; li.pop_front(); }
cout<<endl; print(li);
cout<<"Removing 7 and 12 from the list...\n";
[Link](7); [Link](12); print(li);
[Link](); assert([Link]());
ls.push_back("John");ls.push_front("Tom");
ls.push_back("Anna"); ls.push_front("John");
cout<<"\nSorting elements...\n"; [Link]();
print(ls);
print(ls);
cout<<"\nRemoving duplicate elements...\n";
[Link](); print(ls); }
C++
Horizon
346
deques.
is a a double-ended queue
supports random access
allows insert and erase operations in constant time at the head or
tail, and in linear time in the middle.
memory management is handled automatically.
has many similarities with vector but they differ in one
important respect: you can manipulate with both ends of deque
supports random access iterator and [ ] operator is overloaded
Thus a deque object can be indexed like an array, and an element
can be accessed using the subscript notation
also supports reverse iterator
C++
Horizon
347
To obtain an iterator for deque : deque <T>::iterator i
The template specification is :
template <class T, class Allocator=allocator <T> > class deque
where T is the type of data being stored and Allocator specifies
the allocator which defaults to the standard allocator.
Constructors :
deque (const Allocator &a=Allocator() ) Constructs an empty deque.
deque (size_type num, const T &val=T(), const Allocator &a=Allocator() )
Constructs a deque that has num elements with value val
deque (const deque <T, Allocator> &ob) deques copy constructor.
template <class InIter>
deque (InIter start, InIter end, const Allocator &a=Allocator() )
Constructs a deque that contains the elements in the range specified by start
and end.
C++
Horizon
348
Some of the most commonly used member functions are:
iterator begin ()
iterator end ()
bool empty ()
Returns an iterator to the first element in the deque.
Returns an iterator to the end of the deque.
Returns true if the list contains no elements.
reference front()
reference back ()
Returns a reference to the first element in the list
Returns a reference to the last element in the list
iterator erase (iterator i)
iterator erase (iterator start, iterator end)
iterator insert (iterator i, const T &val)
void insert (iterator i, size_type num, const T &val)
template <class InIter> void insert (iterator i, InIter start, InIter end)
void pop_back ()
void pop_front ()
void push_back (const T &val)
void push_front (const T &val)
Removes the last element in the deque.
Removes the first element in the deque.
size_type size ()
Returns the number of elements currently in the
vector.
C++
Horizon
349
#include <deque> #include <string> #include <algorithm>
#include <assert.h> #include <iostream> using namespace std;
template<class T> void print(deque<T> d) {
for(int i = 0; i < [Link](); i++) cout<<d[i]<<"\t";
cout<<endl; }
main() { deque<int> di; deque<string> ds;
for(int i = 0; i < 20; i++) di.push_back(i);
assert(![Link]()); print(di);
cout<<"Changing vals of elements in range 3 thru 7...\n";
for(i = 3; i < 8; i++) di[i] = 100;
print(di);
cout<<"Removing elements in range 1 through 7...\n";
for(i = 0; i < 8; i++) di.pop_front();
print(di);
[Link]();
ds.push_back("John"); ds.push_front("Tom");
ds.push_front("Jane"); ds.push_back("Anna");
assert(![Link]()); print(ds);
cout<<"\nSorting ...\n"; sort([Link](),[Link]());
print(ds);
cout<<"\nRemoving last element...\n"; ds.pop_back();
print(ds); }
C++
Horizon
350
Vectors are the most commonly used sequence containers. They
are sequence containers of choice when the fastest possible random
access is needed, and few if any insertions or deletions are required
at any point other than the end.
The array in C/C++ has one restriction: its size is fixed at compile
time. The vector class solves this problem by allocating memory
as needed. It defines a dynamic array - an array that can grow
during use.
Lists should be used in preference to other sequence containers
when frequent insertions and deletions are required in the middle of
sequences and there is little need to jump around randomly from
one position to another.
Deques are sequence containers of choice when fast random access
is needed, and if insertions or deletions are required at the
beginning and the end of the [Link] is perfect container to use
if you will be frequently moving from one end to the other, for
example, when rotating the contents of the container.
C++
Horizon
351
Associative Containers types :
set<T, Compare> supports unique keys, provides for fast retrieval of
the keys themselves.
multiset<T, Compare> allows equal keys, provides for fast retrieval of
the keys themselves.
map<Key, T, Compare> supports unique keys, provides for fast
retrieval of values of another type T base on the keys.
multimap<Key, T, Compare> supports unique keys, provides for fast
retrieval of values of another type T base on the keys.
Thus : Maps and multimaps allow arbitrary data to be associated with
each key.
Also, maps and sets only allow elements with unique keys
Multimaps and multisets may contain elements with duplicate
keys.
C++
Horizon
352
Maps and Multimaps :
The map class supports an associative container in which unique
keys are mapped with values.
A key is simply a name associated with a value.
Once a value has been stored, it can be retrieved by its key.
The power of a map is that a value can be looked up given its
key.
Example, a map could be defined that uses a persons name as
its key and stores that persons telephone number as its value.
Multimap is similar to map except that it allows duplicate
keys. Thus, one key can be used for more than one value.
C++
Horizon
353
The map class supports bi-directional iterators.
Thus, the container can be accessed through an iterator in both
forward and reverse direction.
Random access operations are not supported.
The map class overloads the assignment operator =. It also defines
comparison operators: ==, <, <=, !=, >, >=
Key/value pairs are stored in a map as objects of type pair.
A pair contains two fields:
Key_type
Value_type
C++
first;
// contains the key
second. // contains the value associated with the key
Horizon
354
The data type of the pair must match those of the map into which
the pair are being inserted.
The iterator type defined by map points to objects of type
pair<Key, T>.
To obtain an iterator for map : map <Key, T>::iterator i
The template specification is :
template <class Key, class T, class Comp=less<Key>,
class Allocator=allocator <T> > class map
where Key is the data type of keys, T is the data type of the value
being stored (mapped), Comp is the function that compares two
keys, and Allocator specifies the allocator which defaults to the
standard allocator.
C++
Horizon
355
Constructors :
map (const Comp &cmpfn=Comp(),const Allocator &a=Allocator() )
Constructs an empty map
map (const map <Key, T, Comp, Allocator> &ob)
map's copy constructor.
template <class InIter>
map (InIter start, InIter end, const Comp &cmpfn=Comp(), const
Allocator &a=Allocator() )
Constructs a map that contains the elements in the range specified by
start and end.
C++
Horizon
356
Some of the most commonly used member functions are:
iterator begin ()
Returns an iterator to the first element in the map.
iterator end ()
Returns an iterator to the end of the map.
bool empty ()
Returns true if the map contains no elements.
iterator find (const key_type &k) Returns an iterator to specified key. If the key is not found, then
an iterator to the end of the map is returned.
iterator erase (iterator i)
iterator erase (iterator start, iterator end)
size_type erase (const key_type &k) Removes from map elements that have keys with value k.
iterator insert (iterator i, const T &val)
template <class InIter> void insert (InIter start, InIter end) Inserts a range of elements defined
by start and end immediately before the element
specified by i.
pair<iterator, bool> insert (const value_type &val) Inserts val into the map only if it is not
already there. If the element is inserted returns
pair<iterator, true>, otherwise, pair<iterator, false>
is returned
size_type size ()
Returns the number of elements currently in the map.
reference operator[ ](const key_type &k) Returns reference to value associated with key
specified by k. If this element does not exist, it is
C++
Horizon
357
inserted.
#include<iostream> #include <map> #include<string>
using namespace std;
template <class T1,class T2>
void print(map<T1,T2> m) {
map<T1,T2>::iterator p = [Link]();
while(p != [Link]()) { cout<<p->first<<"\t"<<p->second<<"\n";
cout<<endl;
}
p++;
main() {
map<string, string> m;
m["John"]="555-1234";
m["Daniel"]="797-3456";
m["Anna"]="990-8901";
print(m);
[Link](pair<string, string>("Tom", "123-4567"));
map<string, string>::iterator p = [Link]("Tom");
if (p != [Link]()) cout<<p->first<<"\t"<<p->second<<endl;
else cout<<"Key Tom not in map.\n";
p = [Link]("Tomy");
if (p != [Link]()) cout<<p->first<<"\t"<<p->second<<endl;
else cout<<"Key Tomy not in map.\n";
}
C++
Horizon
358
Iterators
Iterators provide a way of specifying a position in a container.
An iterator can be incremented or dereferenced, and two iterators
can be compared.
The simplest iterators in STL allow to traverse the contents of a
collection without having to be concerned about the type of the
collection.
Example:
#include <iostream> #include <vector>
using namespace std;
void main()
{ vector<long> coll;
// Declare the collection
coll.push_back(9); coll.push_back(6);
vector<long>::iterator itr; // Declare the iterator
for (itr=[Link](); itr != [Link](); ++itr)
cout << *itr;
}
C++
Horizon
359
Observations of the previous example :
The increment operator ++ is used to walk the collection.
This increment operator causes the compiler to call the member
function operator++(), which is part of the iterator.
Internally, the iterator may traverse a linked list, iterate a pointer,
or perform more complex processing
Iterators are defined as nested classes.
The declaration vector::iterator refers to a nested type within the
vector class template.
Example:
template<class Type>
class vector{
public: class iterator {
public:
iterator();
};
};
C++
Horizon
360
Every collection class defines its own implementation of iterator.
There is no single implementation of iterator that works across all
classes.
Iterators are the glue that connects algorithms to containers.
Iterators are classified into five categories :
forward
bidirectional
random access
input
output.
C++
Horizon
361
Random access iterator
It can be used to store and retrieve values, provide for
bidirectional traversal, and supports the following operations: ++,
--, +, - and can also be indexed via the [ ] operator.
allow the operations of pointer arithmetic: addition of arbitrary
offsets, subscripting, subtraction of one iterator from another to
find a distance, and so on
A random access iterator is the type used by vector.
Bidirectional iterator
This provides for traversal in both directions and allow multi-pass
algorithms.
It may be incremented to obtain the next element or decremented
to obtain the previous element.
It can store & retrieve values & supports ++ and --, but not + or -.
Bidirectional iterators are used by list.
C++
Horizon
362
Forward iterator
This provides for one-directional traversal of a sequence.
It can store and retrieve values and supports ++.
A Forward Iterator may be
constant i.e. it is possible to access the object it points to
but not to modify it
mutable i.e. it is possible to do both accesssing and
modifying the object.
They permit use of "multi-pass" algorithms.
Input iterator
This can obtain values but not alter them.
It can move in a forward direction only and supports ++.
C++
Horizon
363
Output iterator
This can store values, but not obtain them.
It can move in a forward direction only and supports ++.
Input and Output Iterators permit "single pass" algorithms but do
not necessarily support "multi-pass" algorithms
C++
Horizon
364
Category of iterators provided by each STL container type
vector<T>::iterator and deque<T>::iterator are random access
iterator types
list<T>::iterator is a bidirectional iterator type
All the iterator types of the associative containers are bidirectional
C++
Horizon
365
Algorithms .
.computational procedures
Algorithms are independent from data structures, and are all
parameterised by iterator types.
They can work with user defined data structures, as long as
suitable iterators are provided.
Algorithms are completely independent from the containers Provided that a container supports the required iterators, any
algorithm can be used with it.
Instead of developing algorithms for a specific container, they are
developed for a specific iterator category.
C++
Horizon
366
Classification of Algorithms :
Non-mutating sequence operations
including find, for-each, counting and searching operations.
Algorithms like count and search are examples
Mutating sequence operations
including copying, transformations, removing, generation (a
generator function object creates values that are assigned to
container elements), shuffling and partitioning.
Examples are copy, reverse, or swap
Sorting and related operations
including sorting, searching, merging, set, heap, and
lexicographical comparison operations. Sorting functions include
stable sorting, where the order of equal elements is preserved,
unstable sorting, and partial sorting.
Examples are stable_sort, binary_search, and merge
C++
Horizon
367
Numeric operations
such as partial sums, or applying an operator to all elements in a
sequence.
The STL provides algorithms for: accumulate, inner_product,
partial_sum, and adjacent_difference.
Set Operations
mathematical set operations, such as set_union and
set_intersection. These algorithms only work on sorted
containers.
Heap Operations
Heaps are a very useful and efficient data structure that is often
used to implement priority queues. The STL provides
facilities for making heaps and using them.
Miscellaneous Operations
such as min and next_permutation
C++
Horizon
368
Example :
(complete this program as an excercise)
vector<int> v; //... fill v
// Sort the vector
sort([Link](), [Link]());
// Print each element of a vector of integers
void print (int i) {cout << i;}
for_each([Link](), [Link](), print);
//Copy the vector
vector<int> v2([Link]()) ;
copy([Link](), [Link](), [Link]());
//Transform v2 - square each element
int square(int i) { return i * i; }
transform([Link](),[Link](), [Link](), square};
C++
Horizon
369
Function Objects
A Function Object, or Functor is any object that can be called as if it
is a function.
Function object is an ordinary function, or a function pointer or
(more generally) is an object of a class with operator() defined.
Instead of passing a function pointer to an algorithmic template, the
interface accepts an object with operator() defined.
class Func {
public :
int operator()() {cout<<"func object :: \n" ; return 6666 ;}
void operator()(int t) {cout<<"func object :: "<<t<<"\n" ;}
void operator()(int t, int m) {cout<<"func object :: "<<t<<" :: "<<m<<"\n" ;}
};
void main() {
Func func ;
cout<<func()<<endl ;
func(2) ;
func(7, 8) ;
}
C++
Horizon
370
Function objects and function templates greatly increase the power
and efficiency of the library.
Example : a vector v of doubles could be transformed to the vector w
as -:
transform([Link](), [Link](), [Link](), negate<double>());
transform([Link](), [Link](), [Link](), myfunc<double>()); etc
The STL provides function objects for all of the comparison
operators in the language.
Example :
less can be used to sort a container ascendingly and greater to sort it
descendingly.
STL includes many predefined function objects, eg, arithmetic
operations (plus, minus, multiplies, divides, modulus, & negate),
comparisons (equal_to, not_equal_to greater, less, greater_equal, &
less_equal), and logical operations (logical_and, logical_or, & logical_not).
C++
Horizon
371
A simple example :
class less {
int val;
public:
less (int v) : val (v) {}
int operator () (int v) { return v < val; }
};
main( ) {
less less_than_five (5);
cout << "2 is less than 5: " << (less_than_five (2) ? "yes" : "no");
}
Output: 2 is less than 5: yes
C++
Horizon
372
The basic function object are :
Generator : objects that can be called as f( )
Unary Function :objects that can be called as f(x)
Binary Function : objects that can be called as f(x,y)
This list could obviously be extended to ternary function and
beyond, but, in practice, no STL algorithms require function objects
of more than two arguments.
All other function object concepts defined by the STL are
refinements of these three.
Function objects that return bool are an important special case.
A Unary Function whose return type is bool is called a Predicate
A Binary Function whose return type is bool is called a Binary
Predicate.
C++
Horizon
373
Adaptable Function Objects :
An adaptable function object, however, does specify what the
argument and return types are, and provides nested typedefs so that
those types can be named and used in programs.
If F0 is a model of Adaptable Generator, then it must define
F0::result_type.
If F1 is a model of Adaptable Unary Function it must define
F1::argument_type
F1::result_type
If F2 is a model of Adaptable Binary Function it must define
F2::first_argument_type
F2::second_argument_type
F2::result_type.
The STL provides base classes unary_function and binary_function
to simplify the definition of Adaptable Unary Functions and
Adaptable Binary Functions.
C++
Horizon
374
Some more examples :
Fill a vector with random numbers.
[In this example, the function object is simply a function pointer. ]
vector<int> V(100);
generate([Link](), [Link](), rand);
Sort a vector of double.
[In this example, the function object is an object of a user-defined
class.]
struct less_mag : public binary_function<double, double, bool>
{
bool operator()(double x, double y) { return x < y; }
};
vector<double> V(5) ;
sort([Link](), [Link](), less_mag());
It would be necessary to include the following files : vector,
algorithm, functional
C++
Horizon
375
Adaptors
Adaptors are template classes that provide an existing class with a
new interface.
Adaptors can be used to create new interfaces for containers or
iterators.
Adaptors are casting wrappers that change the appearance of a
container (building a stack from a list), or an iterator (converting a
bidirectional iterator to a reverse iterator).
For example, the copy function expects an input iterator to get its
data from. The istream class has the right functionality: it acts as a
source of data, but it has the wrong interface: it uses << etc.
There is an adaptor called istream_iterator that provides the
interface that copy expects, translating requests into istream
operations.
C++
Horizon
376
Container Adaptors
These can be used to create a specialized container from an
existing, more general container.
The operations of the new container are implemented using the
underlying operations of the existing containers.
Thus new containers can be created without much additional effort,
since most of the functionality is already provided by the existing
container.
The STL provides three container adaptors:
stack<Container>
queue<Container>
deque<Container>
C++
Horizon
377
#include <vector>
#include <stack>
Output :
9
main( )
{
stack <vector<int>, vector<int> >
for (int i=0; i < 10; i++)
[Link] (i) ;
while ( ![Link]() )
{
cout << [Link]() << " ;
[Link]() ;
}
cout << endl ;
}
C++
876543210
st ;
Horizon
378
Iterator Adaptors
These adaptors can be used to extend the functionality of an
existing iterator.
The STL provides three iterator adaptors:
Reverse iterators : Random access iterators and bi-directional
iterators can be traversed forwards and backwards. By applying
the reverse_iterator adaptor to either a random access iterator or
a bi-directional iterator an iterator is obtained that will traverse
the same container in the reverse direction.
Insert iterators : Iterators behave a lot like pointers; if you
assign a new value to a container through it's iterator, you will
overwrite any value that is already at the location specified by
the iterator. Sometimes, it is desirable to be able to insert a new
element at that location in the container.
C++
Horizon
379
Raw storage iterators : Raw storage iterators allow algorithms
to use raw, uninitialized memory during their execution. The
raw_storage_iterator is used by several internal algorithms for
partitioning and merging elements in a container.
STL provides three different insert iterators. Each type of adaptor
performs the insertion into the container in a different place.
back_insert_iterators add elements to end of container. The
algorithm uses the back_insert function to do the insertions, it
can only be used with vectors, lists, and deques.
front_insert_iterators add elements to front of container.
push_front is used for insertions - can only be used on lists,
deques.
insert_iterators add elements to container at location specified
when the iterator is constructed. This iterator can be used to
insert new elements anywhere within a container, not just at the
front or the end. It can be used with any of the STL containers.
C++
Horizon
380
Allocator
Allocators encapsulate information on memory models, allocation
and deallocation primitives, and object sizes.
They are an aid to portability, because all of the containers are
parameterised in terms of allocators.
They also enable custom allocation schemes to be used with the
library.
Allocators separate the STL from the dependencies of the
underlying memory model of the machine architecture.
Each container is given an allocator when it is constructed.
C++
Horizon
381
Whenever a container inserts or removes an element, it uses its
allocator to allocate and deallocate the memory for the object.
The container does not know anything about the memory model of
the machine, it relies on the allocator for all of its memory needs.
The details of the allocator interface are still subject to change.
An allocator must be considered as a "black box". That is, a
container's memory allocation strategy may be selected by
instantiating the container template with a particular allocator, but
no assumptions must be made about how the container actually
uses the allocator.
The default allocator, alloc, is usually the best choice.
C++
Horizon
382
The various allocators are as follows :
alloc
The default allocator. It is thread-safe, and usually has the best
performance characteristics.
A thread-safe allocator that uses a different memory pool for
each thread
Can be used only if the operating system provides pthreads.
Usually faster than alloc, especially on multiprocessor systems.
Can, however, cause resource fragmentation: memory
deallocated in one thread is not available for use by other
threads.
single_client_alloc A fast but thread-unsafe allocator.
Programs having one thread, this allocator might be faster than
alloc.
pthread_alloc
malloc_alloc
C++
An allocator that uses the standard library function malloc.
Is thread-safe but slow
Horizon
383
Examples
vector<double> V(100, 5.0); // Uses the default allocator
// Creates a vector containing 100 elements each
// initialised to 5.0
vector<double, single_client_alloc> local([Link](), [Link]());
Template specifications for some of the containers :
template <class T, class Allocator=allocator <T> > class vector
template <class T, class Allocator=allocator <T> > class deque
template <class T, class Allocator=allocator <T> > class list
template <class Key, class T, class Comp=less<Key>, class Allocator=allocator
<T> > class map
C++
Horizon
384
Exercise 1: Fill two containers with same number of int values. Create a new
container, whose elements are sum of appropriate elements in the original
containers.
#include <vector> #include <algorithm> #include <iostream>
#include <functional>
void main (void) {
vector<int> a; vector<int> b;
for (int i = 0; i < 4; i++) {
a.push_back (i*2);
//0, 2, 4, 6
b.push_back ((i+1)*3); //3, 6, 9, 12
}
vector<int> c(4); // allocate memory for 4 int values!!
// use the algo "transform" and the function object "plus"
// transfrom takes the elements of vectors a and b, adds them using
// plus and writes the results to c
transform ([Link](), [Link](), [Link](), [Link](), plus<int>() );
for(i = 0; i < [Link](); i++)
cout<<c[i]<<"\t";
cout<<endl;
}
C++
Horizon
385
Exercise 2: Write a generator object which can be used with the STL
algorithm generate (group 2) to fill containers with certain values. It should
be possible to specify a starting value and a step size, so that the first
element in the container is the starting value and every further element is the
sum of the preceding element and the step size.
#include <vector> #include <algorithm>
#include <iostream.h>
template <class value_type> class gen {
value_type
start, step;
public:
gen() : start(0), step(1) {}
gen (value_type sta, value_type ste) : start(sta), step(ste) {}
value_type operator() () {
value_type tmp = start; start += step; return tmp;
} };
void main () {
vector<int> v(10);
generate ([Link](), [Link](), gen<vector<int>::value_type> (1, 2) );
for(int i=0; i<[Link](); i++) cout<<v[i]<<"\t" ;
cout<<endl ;
}
C++
Horizon
386
Exercise 3 : Create a vector of integer elements, finds the position of the first
zero element in the vector, and then the position of the next zero element in
the vector.
#include <iostream> #include <vector> #include <algorithm>
void main() {
vector<int> v(10); //0,0,0,0,0,0,0,0,0,0
v.push_back(1);
//0,0,0,0,0,0,0,0,0,0,1
v.push_back(5);
//0,0,0,0,0,0,0,0,0,0,1,5
[Link]([Link](), 1) ;
//1,0,0,0,0,0,0,0,0,0,0,1,5
vector<int>::iterator i1 = [Link]();
vector<int>::iterator i2 = [Link]();
vector<int>::iterator first = find(i1, i2, 0); // find first zero occurrence
if(first == i2) cout << "vector has no zeroes" << endl;
else {
// find the second zero occurrence
vector<int>::iterator second = find(first, i2, 0);
if (second == i2)
cout << "vector has one zero" << endl;
else cout << "vector has two or more zeroes" << endl;
}
}
C++
Horizon
387
Exercises
1) Design a library which provides for the following data structures :
vector, stack, list, map (to store a key value pair). Each of these
classes should be defined as template classes. The basic features to
be provided by these classes should be as follows :
addition and deletion of an item
exceptions to be generated when an action becomes illegal
For each class give a complete design before going into
implementation. For example, for a vector you could identify the
following features :
o It is a dynamically adjustable size array.
o An item can be added or deleted only at the end.
o All items are accessible and modifiable.
C++
Horizon
388
1) Define and implement a network protocol as follows :
The protocol defines a connection oriented service i.e., before
the exchange of any data a connection has to be formally
established which needs to be closed explicitly.
Connection is established between two processes running on the
same system.
Data could be transferred in both directions simultaneously i.e.,
a full duplex system.
Data exchanged could be of a variable size.
The protocol would allow an iterative server handle only one
client at a time.
Connection :
A server is identified by its name which has to be known to the
client.
C++
Horizon
389
The server creates a connection object and writes this object in a
global file. This object contains the name of the server
(ServerId). This file could contain multiple such objects.
A client tries a connect function (could be a member of a
connection class) which would read this global file and try to
establish a connection to the server requested by the client
(identified by the server name).
Connection establishment would mean creating a file (named by
the server name) to which data would be written by the client
and then read by the server and vice versa.
The server in turn is always waiting to open such a file
successfully which comes into existence only when the client
creates it.
Test this protocol by creating an echo server.
C++
Horizon
390
1) Develop template functions for the following global algorithms :
Sort : to sort a vector
template <class T, class Comparator>
void SortAscend (vector<T> &v, Comparator Comp)
Sorting would be done on the basis of the greaterthan function
available in the Comparator.
template <class T, class Comparator>
void SortDescend (vector<T> &v, Comparator Comp)
Sorting would be done on the basis of the lessthan function
available in the Comparator.
C++
Merge : to merge two vectors and the result is a sorted vector
template <class T>
vector<T> & Merge(vector<T> &v1, vector<T> &v2)
Horizon
391