You are on page 1of 64

MODULE 5 MCA-204 Object Oriented Programming and C++ ADMN 2009-‘10

MODULE 5
CONSOLE I/O OPERATIONS
C++ supports two complete I/O systems: the one inherited from C and the
object-oriented I/O system defined by C++ (hereafter called simply the C++ I/O
system). Like C-based I/O, C++'s I/O system is fully integrated. The different aspects
of C++'s I/O system, such as console I/O and disk I/O, are actually just different
perspectives on the same mechanism.

5.1 C++ STREAMS


Like the C-based I/O system, the C++ I/O system operates through streams. A
stream is a logical device that either produces or consumes information. A stream is
linked to a physical device by the I/O system. All streams behave in the same way
even though the actual physical devices they are connected to may differ substantially.
Because all streams behave the same, the same I/O functions can operate on virtually
any type of physical device. For example, you can use the same function that writes to
a file to write to the printer or to the screen. The advantage to this approach is that you
need learn only one I/O system.

5.2 THE C++ STREAM CLASSES


As mentioned, Standard C++ provides support for its I/O system in
<iostream>. In this header, a rather complicated set of class hierarchies is defined
that supports I/O operations. The I/O classes begin with a system of template classes.
Once a template class has been defined, specific instances of it can be created. As it
relates to the I/O library, Standard C++ creates two specializations of the I/O template
classes: one for 8-bit characters and another for wide characters. The C++ I/O system
is built upon two related but different template class hierarchies. The first is derived
from the low-level I/O class called basic_streambuf. This class supplies the basic,
low-level input and output operations, and provides the underlying support for the
entire C++ I/O system. Unless you are doing advanced I/O programming, you will not
need to use basic_streambuf directly. The class hierarchy that you will most
commonly be working with is derived from basic_ios. This is a high-level I/O class
that provides formatting, error checking, and status information related to stream I/O.
(A base class for basic_ios is called ios_base, which defines several nontemplate
traits used by basic_ios.) basic_ios is used as a base for several derived classes,
including basic_istream, basic_ostream, and basic_iostream. These classes are used
to create streams capable of input, output, and input/output, respectively.
As explained, the I/O library creates two specializations of the template class
hierarchies just described: one for 8-bit characters and one for wide characters. Here is

Dept of Computer Science And Applications, SJCET, Palai 148


MODULE 5 MCA-204 Object Oriented Programming and C++ ADMN 2009-‘10

a list of the mapping of template class names to their character and wide-character
versions.

5.3 UNFORMATTED I/O OPERATIONS


Operators >> and <<
We have used the objects cin and cout(pre-defined in the iostream file) for the
input and output of data of various types. This has been made possible by overloading
the operators >> and << to recognize all the basic C++ types. The >>operator is
overloaded in the istream class and <<is overloaded in the ostream class.The
following is the general format for reading data from the keyboard:
cin>>variable1>>variable>>…..>>variableN
variable1,variable2,…are valid C++ variable names that have been declared
already.This statement will cause the computer to stop the execution and look for
input data from the keyboard.The input data for this statement would be:
data1 data2 ……dataN
The input data are separated by white spaces and should match the type of
variable in the cin list. Spaces, newlines and tabs will be skipped.
The operator >> reads the data character by character and assigns it to the
indicated location. The reading for a variable will be terminated at the encounter of a
white space or a character that does not match the desination type. For example,
consider the following code
int code;
cin>>code;
Suppose the following data is given as input:
4258D
The operator will read the characters upto 8 and the value 4258n is assigned to
code. The character D remains in the inputstream and will be input to the next cin
statement. The general form for displaying data on the screen is:
cout<<item1<<item2<<….<<itemN
The items item1 through itemN may be variables or constants of any basic
type.

5.3.1 PUT() AND GET() FUNCTIONS


The classes istrem and ostream define two member functions get() and put()
respectively to handle the single character input/output operations. There are two
types of get() functions .We can use both get(char*) and get(void) prototypes to fetch
a character including thye blank space, tab and the newline character. The get(char*)
version assigns the input character to its argument and the get(void) version returns
the input character.

Dept of Computer Science And Applications, SJCET, Palai 149


MODULE 5 MCA-204 Object Oriented Programming and C++ ADMN 2009-‘10

Since these functions are members of the input/output stream classes,we must
invoke them using an appropriate object.
Example:
char c;
cin.get(c );//get a character fron keyboard and assign it to c
while(c!=‘\n‘)
{
cout<<c;//display the character on screen
cin.get(c);//get another character
}
This code reads and displays a line of text.Remember, the operator >> can also
be used to read a character but it will skip the white spaces and newline character. The
above while loop will not work properly if the statement
cin>.c;
is used in place of
cin.get(c);
The get(void) version is used as follows:
………..
char c;
c=cin.get();//cin.get(c); replaced
………..
………..
The value returned by the function get() is assigned to the variable c.
The function put(), a member of ostream class, can be used to output a line of
text,character by character . For example,
cout.put(‗x‘);
displays the character x and
cout.put(ch);
displays the value of variable ch.
The variable ch must contain a character value. We can also use a number as an
argument to the function put(). For example,
cout.put(68);
Displays the character D. This statement will convert the int value 68 to a char
value and display the character whose ASCII value is 68.
The following segment of a program reads a line of text from the keyboard and
displays it on the screen.
char c;
cin.get(c);//read a character
while(c!=‘\n‘)
{

Dept of Computer Science And Applications, SJCET, Palai 150


MODULE 5 MCA-204 Object Oriented Programming and C++ ADMN 2009-‘10

cout.put(c);
cin.get(c);
}

5.3.2 GETLINE() AND WRITE() FUNCTIONS


We can read display a line of text more efficiently using the line-oriented
input/output functions getline() and write(). The getline() function reads a whole line
of text that ends with a newline character(transmitted by the RETURN key).This
function can be invoked by using the object cin as follows:
cin.getline(line,size);
This function call invokes the function getline() which reads character input
into the variable line.The reading is terminated as soon as either the new line character
‗\n‘ is encountered or size-1 characters are read (whichever occurs first).The newline
character is read but not saved. Instead, it is replaced by the null character .For
example ,consider the following code:
char name[20];
cin.getline(name,20);
Assume that we have given the following input through the keyboard:
Bjarne Stroustrup<press RETURN>
This input will be read correctly and assigned to the character array name .Let
us suppose the input is as follows:
Object Oriented Programming <press RETURN>
In this case, the input will be terminated after reading the following 19
characters:
Object Oriented Pro
Remember, the two blank spaces contained in the string are also taken into
account.
We can also read strings using the operator >> as follows:
cin>>name;
But remember cin can read strings that do not contain white spaces. This means
that cin can read just one word and not a series of words such as ―Bjarne Stroustrup‖.
But it can read the following string correctly:
Bjarne_Stroustrup
After reading the string, cin automatically adds the terminating null character to
the character array.
The write() function displays an entire line and has the following form:
cout.write(line,size)
The first argument line represents the name of the string to be displayed and the
second argument size indicates the number of characters to display. Note that it does
not stop displaying the characters automatically when the null character is

Dept of Computer Science And Applications, SJCET, Palai 151


MODULE 5 MCA-204 Object Oriented Programming and C++ ADMN 2009-‘10

encountered. If the size is greater than the length of line, then it displays beyond the
bounds of line.
#include<iostream.h>
int main()
{
char *string1=‖C++‖;
char *string2=‖Program‖;
int m=strlen(string1);
int n=strlen(string2);
for(int i=1;i<n;i++)
{
cout.write(string2,i);
cout<<‖\n‖;
}
for(int i=n;i>0;i--){
cout.write(string2,i);
cout<<‖\n‖;}
cout.write(string1,m).write(string2,n);//concatenating strings
cout<<‖\n‖;
//crossing the boundary
cout.write(string1,10);
return 0;
}
Output of program:
p
pr
pro
prog
progr
progra
program
progra
progr
prog
pro
pr
p
C++ program
C++ progra

Dept of Computer Science And Applications, SJCET, Palai 152


MODULE 5 MCA-204 Object Oriented Programming and C++ ADMN 2009-‘10

The last line of the output indicates that the statement


cout.write(string1,10);
Displays more characters than what is contained in string1.
It is possible to concatenate two strings using the write() function.The
statement
cout.write(string1,m).write(string2,n);
is equivalent to the following two statements:
cout.write(string1,m);
cout.write(string2,n);

5.4 FORMATTED CONSOLE I/O OPERATIONS


C++ supports a number of features that could be used for formatting the output. These
features include:
 ios class functions and flags.
 Manipulators.
 User-defined output functions.

5.4.1 IOS CLASS FUNCTIONS AND FLAGS


The ios class contains a large number of member functions that would help us
to format the output in a number of ways. The most important ones among them are
listed in Table 5.1.

Function Task
Width() To specify the required field size for displaying an output value.
precision() To specify the number of digits to be displayed after the decimal point
of a float value.
fill() To specify a character that is used to fill the unused portion of a field.
setf() To specify format flags that can control the form of output
display(such as left-justification and right-justification)
unsetf() To clear the flags specified.

(Table 5.1 ios class functions)

Manipulators are special functions that can be included in the I/O statements to
alter the format parameters of a stream. Table shows some important manipulator
functions that are frequently used. To access these manipulators, the file iomanip
should be included in the program.

Dept of Computer Science And Applications, SJCET, Palai 153


MODULE 5 MCA-204 Object Oriented Programming and C++ ADMN 2009-‘10

Manipulators Equivalent ios function


setw() width()
setprecision() precision()
setfill() fill()
setiosflags() setf()
resetiosflags() unsetf()

(Table 5.2 ios class functions and Manipulators)

In addition to these functions supported by the C++ library, we can create our
own manipulator functions to provide any special output formats.

Defining Field Width: width ()


We can use the width() function to define the width of a field necessary for the
output of an item. Since, it is a member function, we have to use an object to invoke it,
as shown below:
cout.width(w);
Where w is the field width (number of columns).The output will be printed in a
field of w characters wide at the right end of the field. The width() function can
specify the field width for only one item. After printing one item it will revert back to
the default. For example, the statements
cout.width(5);
cout<<543<<12<<‖\n‖;
Will produce the following output:
5 4 3 1 2
The value 543 is printed right-justified in the first five columns. The
specification width(5) does not retain the setting for printing the number 12.This can
be improved as follows:
cout.width(5);
cout<<543;
cout.width(5);
cout<<12<<‖\n‖;
This produces the following output:
5 4 3 1 2
Remember that the field width should be specified for each item separately.
C++ never truncates the values and therefore, if the specified field width is smaller
than the size of the value to be printed, C++ expands the field to fit the value.

Dept of Computer Science And Applications, SJCET, Palai 154


MODULE 5 MCA-204 Object Oriented Programming and C++ ADMN 2009-‘10

Setting Precision:precision()
By default, the floating numbers are printed with six digits after the decimal
point. However, we can specify the number of digits to be displayed after the decimal
point while printing the floating-point numbers. This can be done by using the
precision() member function as follows:
cout.precision(d);
Where d is the number of digits to the right of the decimal point. For example
,the statements
cout.precision(3);
cout<<sqrt(2)<<‖\n‖;
cout<<3.14159<<‖\n‖;
cout<<2.50032<<‖\n‖;
will produce the following output:
1.141(truncated)
3.142(rounded to the nearest cent)
2.5(no trailing zeros)
Not that, unlike the function width(),precision() retains the setting in effect
until it is reset. That is why we have declared only one statement for the precision
setting which is used by all the three outputs.
We can set different values to different precision as follows:
cout.precision(3);
cout<<sqrt(2)<<‖\n‖;
cout.precision(5);//reset the precision
cout<<3.14159<<‖\n‖;
We can also combine the field specification with the precision setting.
Example:
cout.precision(2);
cout.width(5);
cout<<1.2345;
The first two statements instruct:‖ print two digits after the decimal point in a
field of five character width‖. Thus, the output will be:
1 2 3

Filling and Padding :fill()


We have been printing the values using much larger field widths than required
by the values. The unused positions of the field are filled with white spaces, by
default. However, we can use the fill() function to fill the unused positions by any
desired character.It is used in the following form:
cout.fill(ch);

Dept of Computer Science And Applications, SJCET, Palai 155


MODULE 5 MCA-204 Object Oriented Programming and C++ ADMN 2009-‘10

Where ch represents the character which is used for filling the unused
positions. Example:
cout.fill(‗*‘);
cout.width(10);
cout<<5250<<‖\n‖;
The output would be:
* * * * * * 5 2 5 0
Financial institutions and banks use this kind of padding while printing cheques
so that no one can change the amount easily.

Formatting Flags, Bit-fields and setf()


We have seen that when the function width() is used ,the value(whether text
or number) is printed right-justified in the field width created. But, it is a usual
practice to print the text left-justified. The setf(),a member function of the ios class
used for this.
The setf() function can be used as follows:
cout.setf(arg1,arg2);
The arg1 is one of the formatting flags defined in the class ios. The
formatting flag specifies the format action required for the output. Another ios
constant, arg2, known as bit field specifies the group to which the formatting flag
belongs.
Table: 5.3 shows the bit fields, flags and their format actions. There are
three bit fields and each has a group of format flags which are mutually exclusive
Examples:
cout.setf(ios::left,ios::adjustfied);
cout.setf(ios::scientific,ios::floatfield);
Note that the first argument should be one of the group members of the
second argument.
Format required Flag(arg1) Bit-Field(arg2)
Left-justified output ios::left ios::adjustfield
Right-justified output ios::right ios::adjustfield
Padding after sign or base ios::internal ios::adjustfield
Indicater(like +##20)
Scientific notation ios::scientific ios::floatfield
Fixed point notation ios::fixed ios::floatfield
Decimal base ios::doc ios::basefield
Octal base ios::oct ios::basefield
Hexadecimal base ios::hex ios::basefield

(Table 5.3 The bit fields, flags and their format actions)

Dept of Computer Science And Applications, SJCET, Palai 156


MODULE 5 MCA-204 Object Oriented Programming and C++ ADMN 2009-‘10

Consider the following segment of code:


cout.filll(‗*‘);
cout.setf(ios::left,ios::adjustfield);
cout.width(15);
cout<<‖TABLE1‖<<‖\n‖;

This will produce the following output:


T A B L E 1 * * * * * * * *
The statements
cout.fill(‗*‘);
cout.precision(3);
cout.setf(ios::internal,ios::adjustfield);
cout.setf(ios::scientific,ios::floatfield);
cout.width(15);
cout<<-12.34567<<‖\n‖;
Will produce the following output:
- * * * * * 1 . 2 3 5 e + 0 1

Displaying Trailing Zeros And Plus Sign


If we print the numbers 10.75, 25.00 and 15.50 using a field width of, say,
eight positions, with two digits precision, and then the output will be as follows:

1 0 . 7 5
2 5
1 5 . 5

Note that the trailing zeros in the second and third items have been truncated.
Certain situations, such as a list of prices of items or the salary statement of
employees, require trailing zeros to be shown. The above output would look better if
they are printed as follows:
10.75
25.00
15.50
The setf() can be used with the flag ios::showpoint as a single argument to
achieve this form of output. For example,
cout.setf(ios::showpoint);//display trailing zeros

Dept of Computer Science And Applications, SJCET, Palai 157


MODULE 5 MCA-204 Object Oriented Programming and C++ ADMN 2009-‘10

Would cause cout to display trailing zeros and trailing decimal point. Under
default precision, the value 3.25 will be displayed as 3.250000. Remember, the default
precision assumes a precision of six digits.
Similarly, a plus sign can be printed before a positive number using the
following statement:
cout.setf(ios::showpos);//show + sign
For example, the statements
cout.setf(ios::showpoint);
cout.setf(ios::showpos);
cout.precision(3);
cout.setf(ios::fixed,ios::floatfield);
cout.setf(ios::internal,ios::adjustfield);
cout.width(10);
cout<<275.5<<‖\n‖;
Will produce the following output:
+ 2 7 5 . 5 0 0

The flags such as showpoint and showpos do not have any bit fields and
therefore are used as single arguments in setf().This is possible because the setf() has
been declared as an overloaded function in the class ios. Table 5.4 lists the flags that
do not possess a named bit field. These flags are not mutually exclusive and therefore
can be set or cleared independently.
Flag Meaning
ios::showbase Use base indicator on output
ios::showpos Print + before positive numbers
ios::showpoint Show trailing decimal point and zeroes
ios::uppercase Use uppercase letters for hex output
ios::skipus skip white space on input
ios::unitbuf Flush all streams after insertion
ios::stdio Flush stdout and stderr after insertion

(Table 5.4 Lists the flags that do not possess a named bit field)

5.4.2 MANAGING OUTPUT WITH MANIPULATORS


The header file iomanip provides a set of functions called manipulators
which can be used to manipulate the output formats. They provide the same features
as that of the ios member functions and flags. Some manipulators are more convenient
to use than their counterparts in the class ios. For example, two or more manipulators
can be used as a chain in one statement as shown below:
cout<<manip1<<manip2<<manip3<<item;

Dept of Computer Science And Applications, SJCET, Palai 158


MODULE 5 MCA-204 Object Oriented Programming and C++ ADMN 2009-‘10

cout<<manip1<<item1<<manip2<<item2;
This kind of concatenation is useful when we want to display several
columns of output.
The most commonly used manipulators are shown in Table 5.5 .The table
also gives their meaning and equivalents. To access these manipulators, we must
include the file iomanip in the program.

Manipulator Meaning Equivalent


setw(int w) Set the field width to w width()
setprecision(int d) Set the floating point precision to d. precision()
setfill(int c) Set the fill character to c fill()
setiosflags(long f) Set the format flag f setf()
resetiosflags(long f) Clear the flag specified by f unsetf()
Endif Insert new line and flush stream ―\n‖

(Table 5.5 Manipulators )

Some examples of manipulators are given below:


cout<<setw(10)<<12345;
This statement prints the value 12345 right-justified in a field width of 10
characters.The output can be made left-justified by modifying the statement as
follows:
cout<<setw(10)<<setiosflags(ios::left)<<12345;
One statement can be used to format output for two or more values.For
example,the statement
cout<<setw(5)<<setprecision(2)<<1.2345<<setw(10)<< setprecision(4)<<sqrt(2)
<<setw(15)<<setiosflags(ios::scientific)<<sqrt(3)<<endl;
Will print all the three values in one line with the field size of 5, 10, and 15
respectively. Note that each output is controlled by different sets of format
specifications.
There is a major difference in the way the manipulators are implemented as
compared to the ios member functions. The ios member function return the previous
format state which can be used later. In case, we need to save the old format states,
we must use the ios member function rather than the manipulators.
Example:
cout.precision(2);//previous state
int p=cout.precision(4);//current state;
When these statements are executed, p will hold the value of 2(previous
state) and the new format state will be 4.We can restore the previous format state as
follows:

Dept of Computer Science And Applications, SJCET, Palai 159


MODULE 5 MCA-204 Object Oriented Programming and C++ ADMN 2009-‘10

cout.precision(p)//p=2

5.4.3 DESIGNING OUR OWN MANIPULATORS


We can design our own manipulators for certain special purpose.The general
form for creating a manipulator without any arguments is:
ostream & manipulator(ostream & output)
{
…………
…………(code)
…………
return output
}
Here the manipulator is the name of the manipulator under creation.The
following function defines a manipulator called unit that dispalys‖inches‖:
ostream & unit(ostream &output)
{
output<<‖inches‖;
return output;
}
The statement
cout<<36<<unit;
Will produce the following output
16 inches
We can also create manipulators that could represent a sequence of
operations. Example:
ostream & show(ostream & output)
{
output.setf(ios::showpoint);
output.setf(ios::showpos);
output<<setw(10);
return output;
}
This function defines a manipulator called show that turns on the flags
showpoint and showpos declared in the class ios and sets the field width to 10.
Program illustrates the creation and use of the user-defined manipulators.
The program creates two manipulators called currency and form which are used in the
main program.
#include<iostream.h>
#include<iomanip.h>
ostream & currency(ostream & output)

Dept of Computer Science And Applications, SJCET, Palai 160


MODULE 5 MCA-204 Object Oriented Programming and C++ ADMN 2009-‘10

{
output<<‖Rs‖;
return output;
}
ostream& form(ostream & output)
{
output.setf(ios::showpos);
output.setf(ios::showpoint);
output.fill(‗*‘);
output.precision(2);
output<<setiosflags(ios::fixed)<<setw(10);
return output;
}
int main()
{
cout<<currency <<form<<7864.5;
return 0;
}

The output of Program would be:


Rs**+7864.50
Note that form represents a complex set of format functions and
manipulators.

5.5 OVERLOADING << AND>> OPERATOR


The << and the >> operators are overloaded in C++ to perform I/O operations
on C++'s built-in types. You can also overload these operators so that they perform I/O
operations on types that you create.
In C++, the << output operator is referred to as the insertion operator because it
inserts characters into a stream. Likewise, the >> input operator is called the
extraction operator because it extracts characters from a stream. The functions that
overload the insertion and extraction operators are generally called inserters and
extractors, respectively.
Creating Our Own Inserters:
All inserter functions have this general form:
ostream &operator<<(ostream &stream, class_type &obj)
{
// body of inserter
return stream;
}

Dept of Computer Science And Applications, SJCET, Palai 161


MODULE 5 MCA-204 Object Oriented Programming and C++ ADMN 2009-‘10

Notice that the function returns a reference to a stream of type ostream., which
is an output stream (ostream is a class derived from ios that supports output.)
Creating Our Own Extractors:
Extractors are the complement of inserters. The general form of an extractor
function is
istream &operator>>(istream &stream, class_type &obj)
{
// body of extractor
return stream;
}
Extractors return a reference to a stream of type istream, which is an input
stream. The first parameter must also be a reference to a stream of type istream.
Example:
Program to overloading << and >> as a non-member function
#include<conio.h>
#include<iostream.h>
class vector
{
int v[10];
public:
vector();
vector(int *x);
friend istream & operator >> (istream &input,vector &b);
friend ostream & operator << (ostream &output,vector &b);
};
vector::vector()
{
for(int i=0;i<5;i++)
v[i]=0;
}
vector::vector(int* x)
{
for(int i=0;i<5;i++)
v[i]=x[i];
}
istream & operator >> (istream &input,vector &b)
{
for(int i=0;i<5;i++)
input>>b.v[i];
return input;

Dept of Computer Science And Applications, SJCET, Palai 162


MODULE 5 MCA-204 Object Oriented Programming and C++ ADMN 2009-‘10

}
ostream & operator<<(ostream &output,vector &b)
{
output<<"("<<b.v[0];
for(int i=1;i<5;i++)
output<<";"<<b.v[i];
output<<")";
return(output);
}
void main()
{
clrscr();
vector vec;
cout<<"\nEnter the elements for vector: ";
cin>>vec;
cout<<"\nElements are";
cout<<vec;
getch();
}

Output:
Enter the elements for vector: 1 2 3 4 5
Elements are (1;2;3;4;5)

5.6 DISK I/O OPERATIONS


Many real – life problems handle large volumes of data and, in such situations,
we need to use some devices such as floppy disk or hard disk to store the data. The
data is stored in these devices using the concept of files. A file is a collection of
related data stored in a particular area on disk. Programs can be designed to perform
the read and write operations on these files.
A program typically involves either or both of the following kinds of data
communication:

1. Data transfer between the console unit and the program.


2. Data transfer between the program and a disk file.

The I/O system of C++ handles file operations which are very much similar to
the console input and output operations. It uses file streams as an interface between
the programs and the files. The stream that supplies data to the program is known as
input stream and the one that receives data from the program is known as output

Dept of Computer Science And Applications, SJCET, Palai 163


MODULE 5 MCA-204 Object Oriented Programming and C++ ADMN 2009-‘10

stream. In other words, the input stream extracts (or reads) data from the file and the
output stream inserts (or writes) data to the file.
This is illustrated in Fig:5.1
Read data Data input
Input stream

Disk files Program

Write data
Output stream
Data output

(Fig:5.1 Input output streams)


The input operation involves the creation of an input stream and linking it with
the program and the input file. Similarly, the output operation involves establishing an
output stream with the necessary links with the program and the output file.

5.6.1 CLASSES FOR FILE STREAM OPERATIONS


The I/O system of C++ contains a set of classes that define the file handling
methods. These include ifstream, ofstream and fstream. These classes are derived
from fstreambase and from the corresponding iostream class. These classes, designed
to manage the disk file, are declared in fstream and therefore we must include this file
in any program that uses files.
Table shows the details of file stream classes. Note that these classes contain
many more features.

Class Contents
filebuf Its purpose is to set the file buffers to read and write. Contain close() and
open() functions
fstreambase Provides operations common to the file streams. Serves as a base for
fstream,ifstream and ofstream class.Contains open() and close() functions
ifstream Provides input operations.Contains open() with default input mode.
Inherits the functions get(), getline(), read(), seekg() and tellg() functions
from istream.

ofstream Provides output operations. Contains open() with default output mode.
Inherits the functions put(), seekp(), write() and tellp() functions from
ostream.

Dept of Computer Science And Applications, SJCET, Palai 164


MODULE 5 MCA-204 Object Oriented Programming and C++ ADMN 2009-‘10

fstream Provides support for simultaneous input and output operations. Contains
open() with default input mode. Inherits all the functions from istream
and ostream classes through iostream

(Table 5.6 File stream classes )

5.7 OPENING AND CLOSING A FILE


If we want to use a disk file, we need to decide the following things about the
file and its intended use:
1. Suitable name for the file.
2. Data type and structure.
3. Purpose.
4. Opening method.
The filename is a string of characters that make up a valid filename for the
operating system. It may contain two parts, a primary name and an optional period
with extension.
For opening a file, we must first create a file stream and then link it to the
filename. A file stream can be defined using the classes ifstream, ofstream and fstream
that are contained in the header file fstream. The class to be used depends upon the
purpose, that is, whether we want to read data from the file or write data to it. A file
can be opened in two ways:
1. Using the constructor function of the class.
2. Using the member function open() of the class.
The first method is useful when we use only one file in the stream. The second
method is used when we want to manage multiple files using one stream.

5.7.1 OPENING FILES USING CONSTRUCTOR


We know that a constructor is used to initialize an object while it is being
created. Here, a filename is used to initialize the file stream object. This involves the
following steps.
1. Create a file stream object to manage the stream using the appropriate class.
That is to say, the class ofstram is used to create the output stream and the class
ifstream to create the input stream.
2. Initialize the file object with the desired filename.
For example, the following statement opens a file named ―results‖ for output:
Ofstream outfile(―results‖);//output only
This creates outfile as an ofstream object that manages the output stream. This
object can be any valid C++ name such as o_file, myfile or fout. This statement also
opens the file results and attaches it to the output stream outfile. This is illustrated in

Dept of Computer Science And Applications, SJCET, Palai 165


MODULE 5 MCA-204 Object Oriented Programming and C++ ADMN 2009-‘10

Fig5.2.
Disk

Output stream
Results
file
outfile
Program

Data
Input stream
file

infile

(Fig 5.2 Two file streams working on separate files)

Similarly, the following statement declares infile as an ifstream object and


attaches it to the file data for reading(input).
ifstream infile(―data‖);//input only
The program may contain statements like:
outfile <<‖TOTAL‖;
outfile<<sum;
infile>>number;
infile>>string;
Program uses a single file for both writing and reading the data. First, it takes
data from the keyboard and writes it to the file. After the writing is completed, the file
is closed. The program again opens the same file, reads the information already
written to it and displays the same on the screen.
#include<iostream.h>
#include<fstream.h>
int main()
{
ofstream outf(―ITEM‖); //connect ITEM file to outf
cout<<‖Enter item name:‖;
char name[30];
cin>>name; //get name from keyboard
outf<<name<<‖\n‖; //write to file ITEM
cout<<‖Enter item cost:‖;
float cost;
cin>>cost; //get cost from keyboard
outf<<cost<<‖\n‖; //write to file ITEM
outf.close();//disconnect ITEM file from outf

Dept of Computer Science And Applications, SJCET, Palai 166


MODULE 5 MCA-204 Object Oriented Programming and C++ ADMN 2009-‘10

ifstream inf(―ITEM‖);// connect ITEM file to inf


inf>>name;//read name from file ITEM
inf>>cost;//read cost from file ITEM
cout<<‖\n‖;
cout<<‖Item name:‖<<name<<‖\n‖;
cout<<‖Item cost:‖<<cost<<‖\n‖;
inf.close();//disconnect ITEM file from inf
return 0;
}
The output of program would be:
Enter item name:CD-ROM
Enter item cost:250
Item name:CD-ROM
Item cost:250

5.7.2 OPENING FILES USING OPEN()


As stated earlier, the function open() can be used to open multiple files that use
the same stream object. For example, we may want to process a set of files
sequentially. In such cases, we may create a single stream object and use it to open
each file in turn. This is done as follows:
file-stream-class stream-object;
stream-object.open(―filename‖);
Example:
ofstream outfile; //Create stream (for output)
outfile.open(―DATA1‖); //Connect stream to DATA1
……………
outfile.close(); //Disconnect stream from DATA1
outfile.open(―DATA2‖); //Connect stream to DATA2
……………
outfile.close(); //Disconnect stream from DATA2
……………
The above program segment opens two files in sequence for writing the data.
Note that the first file is closed before opening the second one. This is necessary
because a stream can be connected to only one file at a time.
#include<iostream.h>
#include<fstream.h>
int main()
{
ofstream fout; //create output stream
fout.open(―country‖); //connect ―country‖ to it

Dept of Computer Science And Applications, SJCET, Palai 167


MODULE 5 MCA-204 Object Oriented Programming and C++ ADMN 2009-‘10

fout<<‖USA\n‖;
fout<<‖UNITED KINGDOM\n‖;
fout<<‖SOUTH KOREA\n‖;
fout.close(); //disconnect ―country‖ and
fout.open(―capital‖);//connect ―capital‖
fout<<‖WASHINGTON\n‖;
fout<<‖LONDON\n‖;
fout<<‖SEOUL\n‖;
fout.close();//disconnect ―capital‖
const int N=80;//size of line
char line[N];
ifstream fin;//create input stream
fin.open(―country‖);//connect ―country‖ to it
cout<<‖contents of country file\n‖;
while(fin)//check end-of-file
{
fin.getline(line,N);//read a line
cout<<line;//display it
}
fin.close();//disconnect ―country‖ and
fin.open(―capital‖);//connect ―capital‖
cout<<‖contents of capital file\n‖;
while(fin)
{
fin.getline(line,N);
cout<<line;
}
fin.close();
return 0;
}

The output of Program:


contents of country file
USA
UNITED KINGDOM
SOUTH KOREA
contents of capital file
WASHINGTON
LONDON
SEOUL

Dept of Computer Science And Applications, SJCET, Palai 168


MODULE 5 MCA-204 Object Oriented Programming and C++ ADMN 2009-‘10

Disk connect one


file to fout
fout

country file Program

capital file fin


Program

connect one
file to fin

(Fig 5.3 Streams working on multiple files)

At times we may require using two or more files simultaneously. For


example, we may require to merge two sorted files into a third sorted file. This means,
both the sorted files have to be kept open for reading and the third one kept open for
writing. In such cases, we need to create two separate input streams for handling the
two input files and one output stream for handling the output file.

#include<iostream.h>
#include<fstream.h>
int main()
{
cons tint SIZE=80;
char line[SIZE];
ifstream fin1,fin2;//create two input streams
fin1.open(―country‖);
fin2.open(―capital‖);
for(int i=1;i<=10;i++)
{
if(fin1.eof()!=0)
{
cout<<‖Exit from country\n‖;
exit(1);
}
fin1.getline(line,SIZE);
cout<<‖Capital of‖<<line;
if(fin2.eof()!=0)
{
cout<<‖Exit from capital\n‖;

Dept of Computer Science And Applications, SJCET, Palai 169


MODULE 5 MCA-204 Object Oriented Programming and C++ ADMN 2009-‘10

exit(1);
}
fin2.getline(line,SIZE);
cout<<line<<‖\n‖;
}
return 0;
}
The output of Program would be:
Capita of USA
WASHINGTON
Capital of UNITED KINGDOM
LONDON
Capital of SOUTH KOREA
SEOUL

5.8 DETECTING END-OF FILE


Detection of the end-of-file condition is necessary for preventing any
further attempt to read data from the file.
while(fin)
As ifstream object, such as fin, returns a value of 0 if any error occurs in the
file operation including the end-of-file condition. Thus, the while loop terminates
when fin returns a value of zero on reaching the end-of-file condition. Remember, this
loop may terminate due to other failures as well.
There is another approach to detect the end-of-file condition.
if(fin1.eof()!=0){exit(1);}
eof() is a member function of ios class. It returns a non-zero value if the
end-of-file (EOF) condition is encountered, and a zero, otherwise. Therefore, the
above statement terminates the program on reaching the end of the file.

5.9 MORE ABOUT OPEN (): FILE MODES


We have used ifstream and ofstream constructors and the function open() to
create new files as well as to open the existing files. Remember, in both these
methods, we used only one argument that was the filename. However, these functions
can take two arguments, the second one for specifying the file mode. The general form
of the function open() with two argument is:
stream-object.open (“filename”,mode);
The second argument mode (called file mode parameter) specifies the
purpose for which the file is opened.

Dept of Computer Science And Applications, SJCET, Palai 170


MODULE 5 MCA-204 Object Oriented Programming and C++ ADMN 2009-‘10

The prototype of these class member functions contains default values for
the second argument and therefore they use the default values in the absence of the
actual values. The default values are as follows:
ios::in for ifstream functions meaning open for reading only.
ios::out for ofstream functions meaning open for writing only.
The file mode parameter can take one (or more) of such constrants defined
in the class ios.
Table lists the file mode parameters and their meanings
Parameter Meaning
ios::app Append to end-of-file
ios::ate Go to end-of-file on opening
ios::binary Binary file
ios::in Open file for reading only
ios::nocreate Open fails if the file does not exit
ios::noreplace Open fails if the file already exits
ios::out Open file for writing only
ios::trunc Delete the contents of the file if it exists

(Table 5.7 File mode parameters)

1. Opening a file in ios::out mode also opens it in the ios::trunk mode by default.
2. Both ios::app and ios::ate take us to the end of the file when it is opened. The
difference between the two parameters is that the ios::app allows us to add data to
the end of the file only, while ios::ate mode permits us to add data or to modify the
existing data anywhere in the file. In both the cases, a file is created by the
specified name, if it does not exit.
3. The parameter ios::app can be used only with the files capable of output
4. Creating a stream using ifstream implies input and creating a stream using
ofstream implies output. So in these cases it is not necessary to provide the mode
parameters.
5. The fstream class does not provide a mode by default and therefore, we must
provide the mode explicitly when using an object of fstream class.
6. The mode can combine two or more parameters using the bitwise OR operator
(symbol |) shown as follows:
fout.open(“data”,ios::app|ios::nocreate)
This opens the file in the append mode but fails to open the file if it does not exit.

Dept of Computer Science And Applications, SJCET, Palai 171


MODULE 5 MCA-204 Object Oriented Programming and C++ ADMN 2009-‘10

5.10 FILE POINTERS


Each file object has associated with it two integer values called the get pointer
and the put pointer. These are also called the current get position and the current put
position, or—if it‘s clear which one is meant—simply the current position. These
values specify the byte number in the file where writing or reading will take place.
(The term pointer in this context should not be confused with normal C++ pointers
used as address variables.) Often you want to start reading an existing file at the
beginning and continue until the end. When writing, you may want to start at the
beginning, deleting any existing contents, or at the end, in which case you can open
the file with the ios::app mode specifier. These are the default actions, so no
manipulation of the file pointers is necessary. However, there are times when you
must take control of the file pointers yourself so that you can read from and write to
Streams and Files an arbitrary location in the file. The seekg() and tellg() functions
allow you to set and examine the get pointer, and the seekp() and tellp() functions
perform these same actions on the put pointer.

5.10.1 FUNCTIONS FOR MANIPULATION OF FILE POINTERS


The file stream classes support the following functions to manage a file poiner:
 seekg() Moves get pointer(input)to a specified location.
 seekp() Moves put pointer(output) to a specified location.
 tellg() Gives the current position of the get pointer.
 tellp() Gives the current position of the put pointer.
For example, the statement
infile.seekg(10);
Moves the file pointer to the byte number 10. Remember, the bytes in a file are
numbered beginning from zero. Therefore, the pointer will be pointing to the 11th byte
in the file.
Consider the following statements:
ofstream fileout;
fileout.open(―hello‖,ios::app);
int p=fileout.tellp();
On execution of these statements ,the output pointer is moved to the end of the
file‖hello‖and the value of p will represent the number of bytes in the file.

Specifying the Offset


The seekg() function can be used in two ways. We‘ve seen the first, where the
single argument represents the position from the start of the file. You can also use it
with two arguments,

Dept of Computer Science And Applications, SJCET, Palai 172


MODULE 5 MCA-204 Object Oriented Programming and C++ ADMN 2009-‘10

seekg(offset,refposition);
seekp(offset,refposition);
Where the first argument represents an offset from a particular location in the
file, and the second specifies the location from which the offset is measured. There are
three possibilities for the second argument:
ios::beg is the beginning of the file,
ios::cur is the current pointer position, and
ios::end is the end of the file.
The statement
seekp(-10, ios::end);
for example, will set the put pointer to 10 bytes before the end of the file
Table lists some sample pointer offset calls and their actions.fout is an ofstream
object.
Seek call Action
fout.seekg(0,ios::beg); Go to start
fout.seekg(0,ios::cur); Stay at the current position
fout.seekg(0,ios::end); Go to the end of file
fout.seekg(m,ios::beg); Move to (m+1)th byte in the file
fout.seekg(m,ios::cur); Go forward by m byte from the current position
fout.seekg(-m,ios::cur); Go backward by m bytes from the current position
fout.seekg(-m,ios::end); Go backward by m bytes from the end

(Table 5.8 Pointer offset calls)

Here‘s an example that uses the two-argument version of seekg() to find a


particular person object in the GROUP.DAT file, and to display the data for that
particular person. Here‘s the listing for SEEKG:
// seekg.cpp
// seeks particular person in file
#include <fstream> //for file streams
#include <iostream>
using namespace std;
class person //class of persons
{
protected:
char name[80]; //person‘s name
int age; //person‘s age
public:
void getData() //get person‘s data
{

Dept of Computer Science And Applications, SJCET, Palai 173


MODULE 5 MCA-204 Object Oriented Programming and C++ ADMN 2009-‘10

cout << ―\n Enter name: ―; cin >> name;


cout << ― Enter age: ―; cin >> age;
}
void showData(void) //display person‘s data
{
cout << ―\n Name: ― << name;
cout << ―\n Age: ― << age;
}
};
int main()
{
person pers; //create person object
ifstream infile; //create input file
infile.open(―GROUP.DAT‖, ios::in | ios::binary); //open file
infile.seekg(0, ios::end); //go to 0 bytes from end
int endposition = infile.tellg(); //find where we are
int n = endposition / sizeof(person); //number of persons
cout << ―\nThere are ― << n << ― persons in file‖;
cout << ―\nEnter person number: ―;
cin >> n;
int position = (n-1) * sizeof(person); //number times size
infile.seekg(position); //bytes from start
//read one person
infile.read((char*) &pers, sizeof(pers) );
pers.showData(); //display the person
cout << endl;
return 0;
}
Here‘s the output from the program, assuming that the GROUP.DAT file
There are 3 persons in file
Enter person number: 2
Name: Rainier
Age: 21
For the user, we number the items starting at 1, although the program starts
numbering at 0; so
person 2 is the second person of the three in the file.

5.11 BINARY I/O


You can write a few numbers to disk using formatted I/O, but if you‘re storing
a large amount of numerical data it‘s more efficient to use binary I/O, in which

Dept of Computer Science And Applications, SJCET, Palai 174


MODULE 5 MCA-204 Object Oriented Programming and C++ ADMN 2009-‘10

numbers are stored as they are in the computer‘s RAM memory, rather than as strings
of characters. In binary I/O an int is stored in 4 bytes, whereas its text version might
be ―12345‖, requiring 5 bytes. Similarly, a float is always stored in 4 bytes, while its
formatted version might be ―6.02314e13‖, requiring10 bytes.
Our next example shows how an array of integers is written to disk and then
read back into memory, using binary format. We use two new functions: write(), a
member of ofstream; and read(), a member of ifstream. These functions think about
data in terms of bytes (type char).
They don‘t care how the data is formatted, they simply transfer a buffer full of
bytes from and to a disk file. The parameters to write() and read() are the address of
the data buffer and its length. The address must be cast, using reinterpret_cast, to type
char*, and the length is the length in bytes (characters), not the number of data items
in the buffer. Here‘s the listing for BINIO:
// binio.cpp
// binary input and output with integers
#include <fstream> //for file streams
#include <iostream>
using namespace std;
const int MAX = 100; //size of buffer
int buff[MAX]; //buffer for integers
int main()
{
for(int j=0; j<MAX; j++) //fill buffer with data
buff[j] = j; //(0, 1, 2, ...)
//create output stream
ofstream os(―edata.dat‖, ios::binary);
//write to it
os.write( reinterpret_cast<char*>(buff), MAX*sizeof(int) );
os.close(); //must close it
for(j=0; j<MAX; j++) //erase buffer
buff[j] = 0;
//create input stream
ifstream is(―edata.dat‖, ios::binary);
//read from it
is.read( (char*)buff, MAX*sizeof(int) );
for(j=0; j<MAX; j++) //check data
if( buff[j] != j )
{ cerr << ―Data is incorrect\n‖; return 1; }
cout << ―Data is correct\n‖;
return 0;}

Dept of Computer Science And Applications, SJCET, Palai 175


MODULE 5 MCA-204 Object Oriented Programming and C++ ADMN 2009-‘10

You must use the ios::binary argument in the second parameter to write() and
read() when working with binary data. This is because the default, text mode, takes
some liberties with the data. For example, in text mode the ‗\n‘ character is expanded
into two bytes—a carriagereturn and a linefeed—before being stored to disk. This
makes a formatted text file more readable by DOS-based utilities such as TYPE, but it
causes confusion when it is applied to binary data, since every byte that happens to
have the ASCII value 10 is translated into 2 bytes. The ios::binary argument is an
example of a mode bit.

5.12 OBJECT I/O


Since C++ is an object-oriented language, it‘s reasonable to wonder how
objects can be written to and read from disk.

5.12.1 WRITING AN OBJECT TO DISK


When writing an object, we generally want to use binary mode. This writes the
same bit configuration to disk that was stored in memory, and ensures that numerical
data contained in objects is handled properly. Here‘s the listing for OPERS, which
asks the user for information about an object of class person, and then writes this
object to the disk file PERSON.DAT:
// opers.cpp
// saves person object to disk
#include <fstream> //for file streams
#include <iostream>
using namespace std;
class person //class of persons
{
protected:
char name[80]; //person‘s name
short age; //person‘s age
public:
void getData() //get person‘s data
{
cout << ―Enter name: ―; cin >> name;
cout << ―Enter age: ―; cin >> age;
}
};
int main()
{
person pers; //create a person
pers.getData(); //get data for person

Dept of Computer Science And Applications, SJCET, Palai 176


MODULE 5 MCA-204 Object Oriented Programming and C++ ADMN 2009-‘10

//create ofstream object


ofstream outfile(―PERSON.DAT‖, ios::binary);
//write to it
outfile.write((char *)&pers, sizeof(pers));
return 0;
}
The getData() member function of person is called to prompt the user for
information, which it places in the pers object. Here‘s some sample interaction:
Enter name: Coleridge
Enter age: 62
The contents of the pers object are then written to disk, using the write()
function. We use the sizeof operator to find the length of the pers object.

5.12.2 READING AN OBJECT FROM DISK


Reading an object back from the PERSON.DAT file requires the read()
member function. Here‘s the listing for IPERS:
// ipers.cpp
// reads person object from disk
#include <fstream> //for file streams
#include <iostream>
using namespace std;
class person //class of persons
{
protected:
char name[80]; //person‘s name
short age; //person‘s age
public:
void showData() //display person‘s data
{
cout << ―Name: ― << name << endl;
cout << ―Age: ― << age << endl;
}
};
int main()
{
person pers; //create person variable
ifstream infile(―PERSON.DAT‖, ios::binary); //create stream
//read stream
infile.read( (char*)&pers, sizeof(pers) );
pers.showData(); //display person

Dept of Computer Science And Applications, SJCET, Palai 177


MODULE 5 MCA-204 Object Oriented Programming and C++ ADMN 2009-‘10

return 0;
}
The output from IPERS reflects whatever data the OPERS program placed in
the PERSON.DAT file:
Name: Coleridge
Age: 62

5.13 ERROR HANDLING DURING FILE OPERATIONS


So far have been opening and using the files for reading and writing on the
assumption that everything is fine with the files.This may not be true always.For-
instance,one of the following things may happen when dealing with the files.

1. A file which we are attempting to open for reading does not exist.
2. The file name used for a new file may already exist.
3. We may attempt an invalid operation such as reading past the end-of-file.
4. There may not be any space in the disk for sorting more data.
5. We may use an invalid file name.
6. We may attempt to perform an operation when the file is not opened for that
purpose.
The C++ file stream inherits a ‗stream-state‘ member from the class ios.This
member records information on the status of a file that is being currently used.The
stream state member uses bit fields to store the status of the error conditions stated
above.
The class ios supports several member functions that can be used to read the
status recorded in a file stream. These functions along with their meanings are listed in
Table.
Function Return value and meaning
Returns true(non-zero value)if end-of-file is encountered
eof() while reading;
Otherwise returns false(zero)
fail() Returns true when an input or output operation has failed
Returns true if an invalid operation is attempted or any
unrecoverable error has occurred. However, if it is false, it
bad()
may be possible to recover from any other error reported,
and continue operation.
Returns true if no error has occurred. This means, all the
good() above functions are false. For instance, if file.good() is
true, all is well with the stream file and we can proceed to

Dept of Computer Science And Applications, SJCET, Palai 178


MODULE 5 MCA-204 Object Oriented Programming and C++ ADMN 2009-‘10

perform I/O opertions. When it returns false, no further


opertions can be carried out.

(Table 5.9 Error handling functions)

These functions may be used in the appropriate places in a program to locate


the status of a file stream and thereby to take the necessary corrective measures.
Example:

…………
…………
ifstream infile;
infile.open(―ABC‖);
while(!infile.fail())
{
………
………(process the file)
………
}
if(infile.eof())
{
……….(terminate program normally)
}
else
if(infile.bad())
{
…….(report fatal error)
}
else
{
infile.clear();//clear error state
………
………
}
………
The function clear() resets the error state so that further operations can be
attempted.
Remember that we have already used statements such as
while(infile)
{
………

Dept of Computer Science And Applications, SJCET, Palai 179


MODULE 5 MCA-204 Object Oriented Programming and C++ ADMN 2009-‘10

………
}
and
while(infile.read(……))
{
…….
…….
}
Here, infile becomes false (zero)when end of the file is reached (and eof()
becomes true).

5.14 COMMAND-LINE ARGUMENTS


Like C, C++ too supports a feature that facilitates the supply of arguments to
the main() function. These arguments are supplied at the time of invoking the
program. They are typically used to pass the names of data files. Example:
C>exam data results
Here, exam is the name of the file containing the program to be executed, and
data and results are the filenames passed to the program as command-line arguments.
The command-line arguments are typed by the user and are delimited by a space. The
first argument is always the filename (command name)and contains the program to be
executed.
The main() functions which we have been using up to now without any
arguments can take two arguments as shown below:
main(int argc,char *argv[])
The first argument argc(known as argument counter)represents the number of
arguments in the command line.The second argument argv(known as argument
vector)is an array of char type pointers that points to the command line arguments.The
size of this array will be equal to the value of argc.For instance,for the command line
C>exam data results
the value of argc would be 3 and the argv would be an array of three pointers to
strings as shown below:
argv[0]  exam
argv[1]  data
argv[2]  results
Note that argv[0] always represents the command name that invokes the
program.The character pointer argv[1] and argv[2] can be used as filenames in the file
opening statement as shown below:
………….
………….
infile.open(argv[1]);//open data file for reading

Dept of Computer Science And Applications, SJCET, Palai 180


MODULE 5 MCA-204 Object Oriented Programming and C++ ADMN 2009-‘10

………….
………….
outfile.open(argv[2]);//open results file for writing
………….
………….
Program illustrates the use of the command-line arguments for supplying the
file names. The command line is
test ODD EVEN
The program creates two files called ODD and EVEN using the command-line
arguments, and a set of numbers stored in an array are written to these files. Note that
the odd numbers are written to the file ODD and the even numbers are written to the
file EVEN. The program then displays the contents of the files.
#include<iostream.h>
#include<fstream.h>
int main(int argc,char *argv[])
{
int number[9]={11,22,33,44,55,66,77,88,99};
if(argc!=3)
{
cout<<‖argc=‖<<argc<<‖\n‖;
cout<<‖Error in arguments\n‖;
exit(1);
}
ofstream fout1,fout2;
fout1.open(argv[1]);
if(fout1.fail())
{
cout<<‖could not open the file‖<<argv[1]<<‖\n‖;
exit(1);
}
fout2.open(argv[2]);
if(fout2.fail())
{
cout<<‖could not open the file‖<<argv[2]<<‖\n‖;
exit(1);
}
for(int i=0;i<9;i++)
{
if(number[i]%2==0)
fout2<<number[i]<<‖ ‖;//write to EVEN file

Dept of Computer Science And Applications, SJCET, Palai 181


MODULE 5 MCA-204 Object Oriented Programming and C++ ADMN 2009-‘10

else
fout1<<number[i]<<‖ ‖;//write to ODD file
}
fout1.close();
fout2.close();
ifstream fin;
char ch;
for(i=1;i<argc;i++)
{
fin.open(argv[i]);
cout<<‖Contents of ―<<argv[i]<<‖\n‖;
do
{
fin.get(ch);//read a value
cout<<ch;//display it
}
while(fin);
cout<<‖\n\n‖;
fin.close();
}
return 0;
}
The output of program would be
contents of ODD
11 33 55 77 99
contents of EVEN
22 44 66 88

5.15 TEMPLATES
The template is one of C++'s most sophisticated and high-powered features.
Although not part of the original specification for C++, it was added several years ago
and is supported by all modern C++ compilers. Using templates, it is possible to
create generic functions and classes. In a generic function or class, the type of data
upon which the function or class operates is specified as a parameter. Thus, you can
use one function or class with several different types of data without having to
explicitly recode specific versions for each data type.

Dept of Computer Science And Applications, SJCET, Palai 182


MODULE 5 MCA-204 Object Oriented Programming and C++ ADMN 2009-‘10

5.15.1 GENERIC FUNCTIONS


A generic function defines a general set of operations that will be applied to
various types of data. The type of data that the function will operate upon is passed to
it as a parameter. Through a generic function, a single general procedure can be
applied to a wide range of data. As you probably know, many algorithms are logically
the same no matter what type of data is being operated upon. By creating a generic
function, you can define the nature of the algorithm, independent of any data. Once
you have done this, the compiler will automatically generate the correct code for the
type of data that is actually used when you execute the function. In essence, when you
create a generic function you are creating a function that can automatically overload
itself. A generic function is created using the keyword template. The normal meaning
of the word "template" accurately reflects its use in C++. It is used to create a template
(or framework) that describes what a function will do, leaving it to the compiler to fill
in the details as needed. The general form of a template function definition is shown
here:
template <class Ttype> ret-type func-name(parameter list)
{
// body of function
}
Here, Ttype is a placeholder name for a data type used by the function. This
name may be used within the function definition. However, it is only a placeholder
that the compiler will automatically replace with an actual data type when it creates a
specific version of the function. Although the use of the keyword class to specify a
generic type in a template declaration is traditional, you may also use the keyword
typename. The following example creates a generic function that swaps the values of
the two variables with which it is called. Because the general process of exchanging
two values is independent of the type of the variables, it is a good candidate for being
made into a generic function.
// Function template example.
#include <iostream>
using namespace std;
// This is a function template.
template <class X> void swapargs(X &a, X &b)
{
X temp;
temp = a;
a = b;
b = temp;
}

Dept of Computer Science And Applications, SJCET, Palai 183


MODULE 5 MCA-204 Object Oriented Programming and C++ ADMN 2009-‘10

int main()
{
int i=10, j=20;
double x=10.1, y=23.3;
char a='x', b='z';
cout << "Original i, j: " << i << ' ' << j << '\n';
cout << "Original x, y: " << x << ' ' << y << '\n';
cout << "Original a, b: " << a << ' ' << b << '\n';
swapargs(i, j); // swap integers
swapargs(x, y); // swap floats
swapargs(a, b); // swap chars
cout << "Swapped i, j: " << i << ' ' << j << '\n';
cout << "Swapped x, y: " << x << ' ' << y << '\n';
cout << "Swapped a, b: " << a << ' ' << b << '\n';
return 0;
}
Let's look closely at this program. The line: template <class X> void
swapargs(X &a, X &b) tells the compiler two things: that a template is being created
and that a generic definition is beginning. Here, X is a generic type that is used as a
placeholder. After the template portion, the function swapargs() is declared, using X
as the data type of the values that will be swapped. In main() , the swapargs()
function is called using three different types of data: ints, doubles, and chars.
Because swapargs() is a generic function, the compiler automatically creates three
versions of swapargs() : one that will exchange integer values, one that will exchange
floating-point values, and one that will swap characters. Here are some important
terms related to templates. First, a generic function (that is, a function definition
preceded by a template statement) is also called a template function. When the
compiler creates a specific version of this function, it is said to have created a
specialization. This is also called a generated function. The act of generating a
function is referred to as instantiating it. Put differently, a generated function is a
specific instance of a template function. Since C++ does not recognize end-of-line as a
statement terminator, the template clause of a generic function definition does not
have to be on the same line as the function's name. The following example shows
another common way to format the swapargs() function.
template <class X>
void swapargs(X &a, X &b)
{
X temp;
temp = a;
a = b;

Dept of Computer Science And Applications, SJCET, Palai 184


MODULE 5 MCA-204 Object Oriented Programming and C++ ADMN 2009-‘10

b = temp;
}
If you use this form, it is important to understand that no other statements can
occur between the template statement and the start of the generic function definition.
For example, the fragment shown next will not compile.
// This will not compile.
template <class X>
int i; // this is an error
void swapargs(X &a, X &b)
{
X temp;
temp = a;
a = b;
b = temp;
}
As the comments imply, the template specification must directly precede the
function definition.

5.15.2 A FUNCTION WITH TWO GENERIC TYPES


You can define more than one generic data type in the template statement by
using a comma-separated list. For example, this program creates a template function
that has two generic types.
#include <iostream>
using namespace std;
template <class type1, class type2>
void myfunc(type1 x, type2 y)
{
cout << x << ' ' << y << '\n';
}
int main()
{
myfunc(10, "I like C++");
myfunc(98.6, 19L);
return 0;
}
In this example, the placeholder types type1 and type2 are replaced by the
compiler with the data types int and char *, and double and long, respectively, when
the compiler generates the specific instances of myfunc() within main() .

Dept of Computer Science And Applications, SJCET, Palai 185


MODULE 5 MCA-204 Object Oriented Programming and C++ ADMN 2009-‘10

When you create a template function, you are, in essence, allowing the
compiler to generate as many different versions of that function as are necessary for
handling the various ways that your program calls the function.

5.15.3 EXPLICITLY OVERLOADING A GENERIC FUNCTION


Even though a generic function overloads itself as needed, you can explicitly
overload one, too. This is formally called explicit specialization. If you overload a
generic function, that overloaded function overrides (or "hides") the generic function
relative to that specific version. For example, consider the following revised version
of the argumentswapping example shown earlier.
// Overriding a template function.
#include <iostream>
using namespace std;Remember
template <class X> void swapargs(X &a, X &b)
{
X temp;
temp = a;
a = b;
b = temp;
cout << "Inside template swapargs.\n";
}
// This overrides the generic version of swapargs() for ints.
void swapargs(int &a, int &b)
{
int temp;
temp = a;
a = b;
b = temp;
cout << "Inside swapargs int specialization.\n";
}
int main()
{
int i=10, j=20;
double x=10.1, y=23.3;
char a='x', b='z';
cout << "Original i, j: " << i << ' ' << j << '\n';
cout << "Original x, y: " << x << ' ' << y << '\n';
cout << "Original a, b: " << a << ' ' << b << '\n';
swapargs(i, j); // calls explicitly overloaded swapargs()
swapargs(x, y); // calls generic swapargs()

Dept of Computer Science And Applications, SJCET, Palai 186


MODULE 5 MCA-204 Object Oriented Programming and C++ ADMN 2009-‘10

swapargs(a, b); // calls generic swapargs()


cout << "Swapped i, j: " << i << ' ' << j << '\n';
cout << "Swapped x, y: " << x << ' ' << y << '\n';
cout << "Swapped a, b: " << a << ' ' << b << '\n';
return 0;
}
This program displays the following output.
Original i, j: 10 20
Original x, y: 10.1 23.3
Original a, b: x z
Inside swapargs int specialization.
Inside template swapargs.
Inside template swapargs.
Swapped i, j: 20 10
Swapped x, y: 23.3 10.1
Swapped a, b: z x
As the comments inside the program indicate, when swapargs(i, j) is called, it
invokes the explicitly overloaded version of swapargs() defined in the program. Thus,
the compiler does not generate this version of the generic swapargs() function,
because the generic function is overridden by the explicit overloading. Recently, a
new-style syntax was introduced to denote the explicit specialization of a function.
This new method uses the template keyword. For example, using the new-style
specialization syntax, the overloaded swapargs() function from the preceding
program looks like this.
// Use new-style specialization syntax.
template<> void swapargs<int>(int &a, int &b)
{
int temp;
temp = a;
a = b;
b = temp;
cout << "Inside swapargs int specialization.\n";
}
As you can see, the new-style syntax uses the template<> construct to indicate
specialization. The type of data for which the specialization is being created is placed
inside the angle brackets following the function name. This same syntax is used to
specialize any type of generic function. While there is no advantage to using one
specialization syntax over the other at this time, the new-style is probably a better
approach for the long term.

Dept of Computer Science And Applications, SJCET, Palai 187


MODULE 5 MCA-204 Object Oriented Programming and C++ ADMN 2009-‘10

Explicit specialization of a template allows you to tailor a version of a generic


function to accommodate a unique situation—perhaps to take advantage of some
performance boost that applies to only one type of data, for example. However, as a
general rule, if you need to have different versions of a function for different data
types, you should use overloaded functions rather than templates.

5.15.4 OVERLOADING A FUNCTION TEMPLATE


In addition to creating explicit, overloaded versions of a generic function, you
can also overload the template specification itself. To do so, simply create another
version of the template that differs from any others in its parameter list. For example:
// Overload a function template declaration.
#include <iostream>
using namespace std;
// First version of f() template.
template <class X> void f(X a)
{
cout << "Inside f(X a)\n";
}
// Second version of f() template.
template <class X, class Y> void f(X a, Y b)
{
cout << "Inside f(X a, Y b)\n";
}
int main()
{
f(10); // calls f(X)
f(10, 20); // calls f(X, Y)
return 0;
}
Here, the template for f() is overloaded to accept either one or two parameters.

5.15.5 USING STANDARD PARAMETERS WITH TEMPLATE


FUNCTIONS
You can mix standard parameters with generic type parameters in a template
function. These nongeneric parameters work just like they do with any other function.
For example:
// Using standard parameters in a template function.
#include <iostream>
using namespace std;
const int TABWIDTH = 8;
Dept of Computer Science And Applications, SJCET, Palai 188
MODULE 5 MCA-204 Object Oriented Programming and C++ ADMN 2009-‘10

// Display data at specified tab position.


template<class X> void tabOut(X data, int tab)
{
for(; tab; tab--)
for(int i=0; i<TABWIDTH; i++) cout << ' ';
cout << data << "\n";
}
int main()
{
tabOut("This is a test", 0);
tabOut(100, 1);
tabOut('X', 2);
tabOut(10/3, 3);
return 0;
}
Here is the output produced by this program.
This is a test
100
X
3
In the program, the function tabOut() displays its first argument at the tab
position requested by its second argument. Since the first argument is a generic type,
tabOut() can be used to display any type of data. The tab parameter is a standard,
call-by-value parameter. The mixing of generic and nongeneric parameters causes no
trouble and is, indeed, both common and useful.

5.15.6 GENERIC FUNCTION RESTRICTIONS


Generic functions are similar to overloaded functions except that they are more
restrictive. When functions are overloaded, you may have different actions performed
within the body of each function. But a generic function must perform the same
general action for all versions—only the type of data can differ. Consider the
overloaded functions in the following example program. These functions could not be
replaced by a generic function because they do not do the same thing.
#include <iostream>
#include <cmath>
using namespace std;
void myfunc(int i)
{
cout << "value is: " << i << "\n";
}

Dept of Computer Science And Applications, SJCET, Palai 189


MODULE 5 MCA-204 Object Oriented Programming and C++ ADMN 2009-‘10

void myfunc(double d)
{
double intpart;
double fracpart;
fracpart = modf(d, &intpart);
cout << "Fractional part: " << fracpart;
cout << "\n";
cout << "Integer part: " << intpart;
}
int main()
{
myfunc(1);
myfunc(12.2);
return 0;
}

5.15.7 APPLYING GENERIC FUNCTIONS


Generic functions are one of C++'s most useful features. They can be applied to
all types of situations. As mentioned earlier, whenever you have a function that
defines a generalizable algorithm, you can make it into a template function. Once you
have done so, you may use it with any type of data without having to recode it.
A Generic Sort
Sorting is exactly the type of operation for which generic functions were
designed. Within wide latitude, a sorting algorithm is the same no matter what type of
data is being sorted. The following program illustrates this by creating a generic
bubble sort. While the bubble sort is a rather poor sorting algorithm, its operation is
clear and uncluttered and it makes an easy-to-understand example. The bubble()
function will sort any type of array. It is called with a pointer to the first element in the
array and the number of elements in the array.
// A Generic bubble sort.
#include <iostream>
using namespace std;
template <class X> void bubble(
X *items, // pointer to array to be sorted
int count) // number of items in array
{
register int a, b;
X t;
for(a=1; a<count; a++)
for(b=count-1; b>=a; b--)

Dept of Computer Science And Applications, SJCET, Palai 190


MODULE 5 MCA-204 Object Oriented Programming and C++ ADMN 2009-‘10

if(items[b-1] > items[b])


{
// exchange elements
t = items[b-1];
items[b-1] = items[b];
items[b] = t;
}
}
int main()
{
int iarray[7] = {7, 5, 4, 3, 9, 8, 6};
double darray[5] = {4.3, 2.5, -0.9, 100.2, 3.0};
int i;
cout << "Here is unsorted integer array: ";
for(i=0; i<7; i++)
cout << iarray[i] << ' ';
cout << endl;
cout << "Here is unsorted double array: ";
for(i=0; i<5; i++)
cout << darray[i] << ' ';
cout << endl;
bubble(iarray, 7);
bubble(darray, 5);
cout << "Here is sorted integer array: ";
for(i=0; i<7; i++)
cout << iarray[i] << ' ';
cout << endl;
cout << "Here is sorted double array: ";
for(i=0; i<5; i++)
cout << darray[i] << ' ';
cout << endl;
return 0;
}
The output produced by the program is shown here.
Here is unsorted integer array: 7 5 4 3 9 8 6
Here is unsorted double array: 4.3 2.5 -0.9 100.2 3
Here is sorted integer array: 3 4 5 6 7 8 9
Here is sorted double array: -0.9 2.5 3 4.3 100.2
As you can see, the preceding program creates two arrays: one integer and one
double. It then sorts each. Because bubble() is a template function, it is automatically

Dept of Computer Science And Applications, SJCET, Palai 191


MODULE 5 MCA-204 Object Oriented Programming and C++ ADMN 2009-‘10

overloaded to accommodate the two different types of data. You might want to try
using bubble() to sort other types of data, including classes that you create. In each
case, the compiler will create the right version of the function for you.

5.15.8 GENERIC CLASSES


In addition to generic functions, you can also define a generic class. When you
do this, you create a class that defines all the algorithms used by that class; however,
the actual type of the data being manipulated will be specified as a parameter when
objects of that class are created.
Generic classes are useful when a class uses logic that can be generalized. For
example, the same algorithms that maintain a queue of integers will also work for a
queue of characters, and the same mechanism that maintains a linked list of mailing
addresses will also maintain a linked list of auto part information. When you create a
generic class, it can perform the operation you define, such as maintaining a queue or
a linked list, for any type of data. The compiler will automatically generate the correct
type of object, based upon the type you specify when the object is created. The
general form of a generic class declaration is shown here:
template <class Ttype> class class-name
{
.
..
}
Here, Ttype is the placeholder type name, which will be specified when a class
is instantiated. If necessary, you can define more than one generic data type using a
comma-separated list.
Once you have created a generic class, you create a specific instance of that
class using the following general form:
class-name <type> ob;
Here, type is the type name of the data that the class will be operating upon.
Member functions of a generic class are themselves automatically generic. You need
not use template to explicitly specify them as such. In the following program, the
stack class is reworked into a generic class. Thus, it can be used to store objects of
any type. In this example, a character stack and a floating-point stack are created, but
any data type can be used.
// This function demonstrates a generic stack.
#include <iostream>
using namespace std;
const int SIZE = 10;
// Create a generic stack class
template <class StackType> class stack

Dept of Computer Science And Applications, SJCET, Palai 192


MODULE 5 MCA-204 Object Oriented Programming and C++ ADMN 2009-‘10

{
StackType stck[SIZE]; // holds the stack
int tos; // index of top-of-stack
public:
stack() { tos = 0; } // initialize stack
void push(StackType ob); // push object on stack
StackType pop(); // pop object from stack
};
// Push an object.
template <class StackType> void stack<StackType>::push(StackType ob)
{
if(tos==SIZE)
{
cout << "Stack is full.\n";
return;
}
stck[tos] = ob;
tos++;
}
// Pop an object.
template <class StackType> StackType stack<StackType>::pop()
{
if(tos==0)
{
cout << "Stack is empty.\n";
return 0; // return null on empty stack
}
tos--;
return stck[tos];
}
int main()
{
// Demonstrate character stacks.
stack<char> s1, s2; // create two character stacks
int i;
s1.push('a');
s2.push('x');
s1.push('b');
s2.push('y');
s1.push('c');

Dept of Computer Science And Applications, SJCET, Palai 193


MODULE 5 MCA-204 Object Oriented Programming and C++ ADMN 2009-‘10

s2.push('z');
for(i=0; i<3; i++) cout << "Pop s1: " << s1.pop() << "\n";
for(i=0; i<3; i++) cout << "Pop s2: " << s2.pop() << "\n";
// demonstrate double stacks
stack<double> ds1, ds2; // create two double stacks
ds1.push(1.1);
ds2.push(2.2);
ds1.push(3.3);
ds2.push(4.4);
ds1.push(5.5);
ds2.push(6.6);
for(i=0; i<3; i++) cout << "Pop ds1: " << ds1.pop() << "\n";
for(i=0; i<3; i++) cout << "Pop ds2: " << ds2.pop() << "\n";
return 0;
}
As you can see, the declaration of a generic class is similar to that of a generic
function. The actual type of data stored by the stack is generic in the class declaration.
It is not until an object of the stack is declared that the actual data type is determined.
When a specific instance of stack is declared, the compiler automatically generates all
the functions and variables necessary for handling the actual data. In this example,
two different types of stacks are declared. Two are integer stacks. Two are stacks of
doubles. Pay special attention to these declarations:
stack<char> s1, s2; // create two character stacks
stack<double> ds1, ds2; // create two double stacks
Notice how the desired data type is passed inside the angle brackets. By
changing the type of data specified when stack objects are created, you can change
the type of data stored in that stack. For example, by using the following declaration,
you can create another stack that stores character pointers.
stack<char *> chrptrQ;
You can also create stacks to store data types that you create. For example, if
you want to use the following structure to store address information,
struct addr
{
char name[40];
char street[40];
char city[30];
char state[3];
char zip[12];
};

Dept of Computer Science And Applications, SJCET, Palai 194


MODULE 5 MCA-204 Object Oriented Programming and C++ ADMN 2009-‘10

then to use stack to generate a stack that will store objects of type addr, use a
declaration like this:
stack<addr> obj;
As the stack class illustrates, generic functions and classes are powerful tools
that you can use to maximize your programming efforts, because they allow you to
define the general form of an object that can then be used with any type of data. You
are saved from the tedium of creating separate implementations for each data type
with which you want the algorithm to work. The compiler automatically creates the
specific versions of the class for you.

5.15.9 AN EXAMPLE WITH TWO GENERIC DATA TYPES


A template class can have more than one generic data type. Simply declare all
the data types required by the class in a comma-separated list within the template
specification. For example, the following short example creates a class that uses two
generic data types.
/* This example uses two generic data types in a
class definition.
*/
#include <iostream>
using namespace std;
template <class Type1, class Type2> class myclass
{
Type1 i;
Type2 j;
public:
myclass(Type1 a, Type2 b) { i = a; j = b; }
void show() { cout << i << ' ' << j << '\n'; }
};
int main()
{
myclass<int, double> ob1(10, 0.23);
myclass<char, char *> ob2('X', "Templates add power.");
ob1.show(); // show int, double
ob2.show(); // show char, char *
return 0;
}
This program produces the following output:
10 0.23
X Templates add power.

Dept of Computer Science And Applications, SJCET, Palai 195


MODULE 5 MCA-204 Object Oriented Programming and C++ ADMN 2009-‘10

The program declares two types of objects. ob1 uses int and double data. ob2
uses a character and a character pointer. For both cases, the compiler automatically
generates the appropriate data and functions to accommodate the way the objects are
created.

5.15.10 THE TYPENAME AND EXPORT KEYWORDS


Recently, two keywords were added to C++ that relate specifically to
templates: typename and export. Both play specialized roles in C++ programming.
Each is briefly examined.
The typename keyword has two uses. First, as mentioned earlier, it can be
substituted for the keyword class in a template declaration. For example, the
swapargs() template function could be specified like this:
template <typename X> void swapargs(X &a, X &b)
{
X temp;
temp = a;
a = b;
b = temp;
}
Here, typename specifies the generic type X. There is no difference between using
class and using typename in this context. The second use of typename is to inform
the compiler that a name used in a template declaration is a type name rather than an
object name. For example,
typename X::Name someObject;
ensures that X::Name is treated as a type name.
The export keyword can precede a template declaration. It allows other files
to use a template declared in a different file by specifying only its declaration rather
than duplicating its entire definition.

5.15.11 THE POWER OF TEMPLATES


Templates help you achieve one of the most elusive goals in programming: the
creation of reusable code. Through the use of template classes you can create
frameworks that can be applied over and over again to a variety of programming
situations. For example, consider the stack class. it could only be used to store integer
values. Even though the underlying algorithms could be used to store any type of data,
the hard-coding of the data type into the stack class severely limited its application.
However, by making stack into a generic class, it can create a stack for any type of
data. Generic functions and classes provide a powerful tool that you can use to
amplify your programming efforts. Once you have written and debugged a template
class, you have a solid software component that you can use with confidence in a

Dept of Computer Science And Applications, SJCET, Palai 196


MODULE 5 MCA-204 Object Oriented Programming and C++ ADMN 2009-‘10

variety of different situations. You are saved from the tedium of creating separate
implementations for each data type with which you want the class to work. While it is
true that the template syntax can seem a bit intimidating at first, the rewards are well
worth the time it takes to become comfortable with it. Template functions and classes
are already becoming commonplace in programming, and this trend is expected to
continue. For example, the STL (Standard Template Library) defined by C++ is, as its
name implies, built upon templates. One last point: although templates add a layer of
abstraction, they still ultimately compile down to the same, high-performance object
code that you have come to expect from C++.

5.16 EXCEPTION HANDLING


Exception handling allows you to manage run-time errors in an orderly fashion.
Using exception handling, your program can automatically invoke an error-handling
routine when an error occurs. The principal advantage of exception handling is that it
automates much of the error-handling code that previously had to be coded "by hand"
in any large program.

5.16.1 EXCEPTION HANDLING FUNDAMENTALS


Exceptions are of two kinds, namely, synchronous exceptions and
asynchronous exceptions. Errors such as ―out-of-range index‖ and ―over-flow‖ belong
to the synchronous type exceptions. The errors that are caused by events beyond the
control of the program (such as keyboard interrupts) are called asynchronous
exceptions. The proposed exception handling mechanism in C++ is designed to handle
only synchronous exceptions.
The purpose of the exception handling mechanism is to provide means to detect
and report an ―exceptional circumstance‖ so that appropriate action can be taken. The
mechanism suggests a separate error handling code that performs the following tasks:
1. Find the problem (Hit the exception).
2. Inform that an error has occurred (Throw the exception).
3. Receive the error information (Catch the exception).
4. Take corrective actions (Handle the exception).
The error handling code basically consists of two segments, one to detect errors
and to throw exceptions, and the other to catch the exceptions and to take appropriate
actions.
C++ exception handling is built upon three keywords: try, catch, and throw.
In the most general terms, program statements that you want to monitor for exceptions
are contained in a try block. If an exception (i.e., an error) occurs within the try
block, it is thrown (using throw). The exception is caught, using catch, and
processed. The following discussion elaborates upon this general description. Code
Dept of Computer Science And Applications, SJCET, Palai 197
MODULE 5 MCA-204 Object Oriented Programming and C++ ADMN 2009-‘10

that you want to monitor for exceptions must have been executed from within a try
block. (Functions called from within a try block may also throw an exception.)
Exceptions that can be thrown by the monitored code are caught by a catch statement,
which immediately follows the try statement in which the exception was thrown. The
general form of try and catch are shown here.
try {
// try block
}
catch (type1 arg) {
// catch block
}
catch (type2 arg) {
// catch block
}
catch (type3 arg) {
// catch block
}
..
.
catch (typeN arg) {
// catch block
}
The try can be as short as a few statements within one function or as all-
encompassing as enclosing the main() function code within a try block (which
effectively causes the entire program to be monitored). When an exception is thrown,
it is caught by its corresponding catch statement, which processes the exception.
There can be more than one catch statement associated with a try. Which catch
statement is used is determined by the type of the exception.
That is, if the data type specified by a catch matches that of the exception, then
that catch statement is executed (and all others are bypassed). When an exception is
caught, arg will receive its value. Any type of data may be caught, including classes
that you create. If no exception is thrown (that is, no error occurs within the try
block), then no catch statement is executed.
The general form of the throw statement is shown here:
throw exception;
throw generates the exception specified by exception. If this exception is to be
caught, then throw must be executed either from within a try block itself, or from any
function called from within the try block (directly or indirectly).
If you throw an exception for which there is no applicable catch statement, an
abnormal program termination may occur. Throwing an unhandled exception causes

Dept of Computer Science And Applications, SJCET, Palai 198


MODULE 5 MCA-204 Object Oriented Programming and C++ ADMN 2009-‘10

the standard library function terminate() to be invoked. By default, terminate() calls


abort() to stop your program, but you can specify your own termination handler.
Here is a simple example that shows the way C++ exception handling operates.
// A simple exception handling example.
#include <iostream>
using namespace std;
int main()
{
cout << "Start\n";
try
{ // start a try block
cout << "Inside try block\n";
throw 100; // throw an error
cout << "This will not execute";
}
catch (int i)
{ // catch an error
cout << "Caught an exception -- value is: ";
cout << i << "\n";
}
cout << "End";
return 0;
}
This program displays the following output:
Start
Inside try block
Caught an exception -- value is: 100
End
Look carefully at this program. As you can see, there is a try block containing
three statements and a catch(int i) statement that processes an integer exception.
Within the try block, only two of the three statements will execute: the first cout
statement and the throw. Once an exception has been thrown, control passes to the
catch expression and the try block is terminated. That is, catch is not called. Rather,
program execution is transferred to it. (The program's stack is automatically reset as
needed to accomplish this.) Thus, the cout statement following the throw will never
execute. Usually, the code within a catch statement attempts to remedy an error by
taking appropriate action. If the error can be fixed, execution will continue with the
statements following the catch. However, often an error cannot be fixed and a catch
block will terminate the program with a call to exit() or abort() . As mentioned, the
type of the exception must match the type specified in a catch statement. For example,

Dept of Computer Science And Applications, SJCET, Palai 199


MODULE 5 MCA-204 Object Oriented Programming and C++ ADMN 2009-‘10

in the preceding example, if you change the type in the catch statement to double, the
exception will not be caught and abnormal termination will occur. This change is
shown here.
// This example will not work.
#include <iostream>
using namespace std;
int main()
{
cout << "Start\n";
try
{ // start a try block
cout << "Inside try block\n";
throw 100; // throw an error
cout << "This will not execute";
}
catch (double i)
{ // won't work for an int exception
cout << "Caught an exception -- value is: ";
cout << i << "\n";
}
cout << "End";
return 0;
}
This program produces the following output because the integer exception will
not be caught by the catch(double i) statement.
Start
Inside try block
Abnormal program termination
An exception can be thrown from outside the try block as long as it is thrown
by a function that is called from within try block. For example, this is a valid
program.
/* Throwing an exception from a function outside the
try block.
*/
#include <iostream>
using namespace std;
void Xtest(int test)
{
cout << "Inside Xtest, test is: " << test << "\n";
if(test) throw test;

Dept of Computer Science And Applications, SJCET, Palai 200


MODULE 5 MCA-204 Object Oriented Programming and C++ ADMN 2009-‘10

}
int main()
{
cout << "Start\n";
try
{ // start a try block
cout << "Inside try block\n";
Xtest(0);
Xtest(1);
Xtest(2);
}
catch (int i)
{ // catch an error
cout << "Caught an exception -- value is: ";
cout << i << "\n";
}
cout << "End";
return 0;
}
This program produces the following output:
Start
Inside try block
Inside Xtest, test is: 0
Inside Xtest, test is: 1
Caught an exception -- value is: 1
End
A try block can be localized to a function. When this is the case, each time the
function is entered, the exception handling relative to that function is reset. For
example, examine this program.
#include <iostream>
using namespace std;
// Localize a try/catch to a function.
void Xhandler(int test)
{
try
{
if(test) throw test;
}
catch(int i)
{

Dept of Computer Science And Applications, SJCET, Palai 201


MODULE 5 MCA-204 Object Oriented Programming and C++ ADMN 2009-‘10

cout << "Caught Exception #: " << i << '\n';


}
}
int main()
{
cout << "Start\n";
Xhandler(1);
Xhandler(2);
Xhandler(0);
Xhandler(3);
cout << "End";
return 0;
}
This program displays this output:
Start
Caught Exception #: 1
Caught Exception #: 2
Caught Exception #: 3
End
As you can see, three exceptions are thrown. After each exception, the function
returns. When the function is called again, the exception handling is reset. It is
important to understand that the code associated with a catch statement will be
executed only if it catches an exception. Otherwise, execution simply bypasses the
catch altogether. (That is, execution never flows into a catch statement.)
For example, in the following program, no exception is thrown, so the catch
statement does not execute.
#include <iostream>
using namespace std;
int main()
{
cout << "Start\n";
try
{ // start a try block
cout << "Inside try block\n";
cout << "Still inside try block\n";
}
catch (int i)
{ // catch an error
cout << "Caught an exception -- value is: ";
cout << i << "\n";

Dept of Computer Science And Applications, SJCET, Palai 202


MODULE 5 MCA-204 Object Oriented Programming and C++ ADMN 2009-‘10

}
cout << "End";
return 0;
}
The preceding program produces the following output.
Start
Inside try block
Still inside try block
End
As you see, the catch statement is bypassed by the flow of execution.

5.16.2 CATCHING CLASS TYPES


An exception can be of any type, including class types that you create.
Actually, in real-world programs, most exceptions will be class types rather than built-
in types. Perhaps the most common reason that you will want to define a class type for
an exception is to create an object that describes the error that occurred. This
information can be used by the exception handler to help it process the error. The
following example demonstrates this.
// Catching class type exceptions.
#include <iostream>
#include <cstring>
using namespace std;
class MyException
{
public:
char str_what[80];
int what;
MyException() { *str_what = 0; what = 0; }
MyException(char *s, int e)
{
strcpy(str_what, s);
what = e;
}
};
int main()
{
int i;
try
{
cout << "Enter a positive number: ";

Dept of Computer Science And Applications, SJCET, Palai 203


MODULE 5 MCA-204 Object Oriented Programming and C++ ADMN 2009-‘10

cin >> i;
if(i<0)
throw MyException("Not Positive", i);
}
catch (MyException e)
{ // catch an error
cout << e.str_what << ": ";
cout << e.what << "\n";
}
return 0;
}
Here is a sample run:
Enter a positive number: -4
Not Positive: -4
The program prompts the user for a positive number. If a negative number is
entered, an object of the class MyException is created that describes the error. Thus,
MyException encapsulates information about the error. This information is then used
by the exception handler. In general, you will want to create exception classes that
will encapsulate information about an error to enable the exception handler to respond
effectively.

5.16.3 USING MULTIPLE CATCH STATEMENTS


As stated, you can have more than one catch associated with a try. In fact, it is
common to do so. However, each catch must catch a different type of exception. For
example, this program catches both integers and strings.
#include <iostream>
using namespace std;
// Different types of exceptions can be caught.
void Xhandler(int test)
{
try
{
if(test)
throw test;
else
throw "Value is zero";
}
catch(int i)
{
cout << "Caught Exception #: " << i << '\n';

Dept of Computer Science And Applications, SJCET, Palai 204


MODULE 5 MCA-204 Object Oriented Programming and C++ ADMN 2009-‘10

}
catch(const char *str)
{
cout << "Caught a string: ";
cout << str << '\n';
}
}
int main()
{
cout << "Start\n";
Xhandler(1);
Xhandler(2);
Xhandler(0);
Xhandler(3);
cout << "End";
return 0;
}
This program produces the following output:
Start
Caught Exception #: 1
Caught Exception #: 2
Caught a string: Value is zero
Caught Exception #: 3
End
As you can see, each catch statement responds only to its own type. In general,
catch expressions are checked in the order in which they occur in a program. Only a
matching statement is executed. All other catch blocks are ignored.

5.16.4 EXCEPTION HANDLING OPTIONS


There are several additional features and nuances to C++ exception handling that
make it easier and more convenient to use. These attributes are discussed here.

5.16.4.1 CATCHING ALL EXCEPTIONS


In some circumstances you will want an exception handler to catch all exceptions
instead of just a certain type. This is easy to accomplish. Simply use this form of
catch.
catch(...)
{
// process all exceptions
}
Dept of Computer Science And Applications, SJCET, Palai 205
MODULE 5 MCA-204 Object Oriented Programming and C++ ADMN 2009-‘10

Here, the ellipsis matches any type of data. The following program illustrates
catch(...).
// This example catches all exceptions.
#include <iostream>
using namespace std;
void Xhandler(int test)
{
try
{
if(test==0) throw test; // throw int
if(test==1) throw 'a'; // throw char
if(test==2) throw 123.23; // throw double
}
catch(...)
{ // catch all exceptions
cout << "Caught One!\n";
}
}
int main()
{
cout << "Start\n";
Xhandler(0);
Xhandler(1);
Xhandler(2);
cout << "End";
return 0;
}
This program displays the following output.
Start
Caught One!
Caught One!
Caught One!
End
As you can see, all three throws were caught using the one catch statement.
One very good use for catch(...) is as the last catch of a cluster of catches. In this
capacity it provides a useful default or "catch all" statement. For example, this slightly
different version of the preceding program explicity catches integer exceptions but
relies upon catch(...) to catch all others.
// This example uses catch(...) as a default.
#include <iostream>

Dept of Computer Science And Applications, SJCET, Palai 206


MODULE 5 MCA-204 Object Oriented Programming and C++ ADMN 2009-‘10

using namespace std;


void Xhandler(int test)
{
try
{
if(test==0) throw test; // throw int
if(test==1) throw 'a'; // throw char
if(test==2) throw 123.23; // throw double
}
catch(int i)
{ // catch an int exception
cout << "Caught an integer\n";
}
catch(...)
{ // catch all other exceptions
cout << "Caught One!\n";
}
}
int main()
{
cout << "Start\n";
Xhandler(0);
Xhandler(1);
Xhandler(2);
cout << "End";
return 0;
}
The output produced by this program is shown here.
Start
Caught an integer
Caught One!
Caught One!
End
As this example suggests, using catch(...) as a default is a good way to catch all
exceptions that you don't want to handle explicitly. Also, by catching all exceptions,
you prevent an unhandled exception from causing an abnormal program termination.

5.16.4.2 RESTRICTING EXCEPTIONS


You can restrict the type of exceptions that a function can throw outside of
itself. In fact, you can also prevent a function from throwing any exceptions

Dept of Computer Science And Applications, SJCET, Palai 207


MODULE 5 MCA-204 Object Oriented Programming and C++ ADMN 2009-‘10

whatsoever. To accomplish these restrictions, you must add a throw clause to a


function definition. The general form of this is shown here:
ret-type func-name(arg-list) throw(type-list)
{
// ...
}
Here, only those data types contained in the comma-separated type-list may be
thrown by the function. Throwing any other type of expression will cause abnormal
program termination. If you don't want a function to be able to throw any exceptions,
then use an empty list. Attempting to throw an exception that is not supported by a
function will cause the standard library function unexpected() to be called. By
default, this causes abort() to be called, which causes abnormal program termination.
The following program shows how to restrict the types of exceptions that can
be thrown from a function.
// Restricting function throw types.
#include <iostream>
using namespace std;
// This function can only throw ints, chars, and doubles.
void Xhandler(int test) throw(int, char, double)
{
if(test==0) throw test; // throw int
if(test==1) throw 'a'; // throw char
if(test==2) throw 123.23; // throw double
}
int main()
{
cout << "start\n";
try
{
Xhandler(0); // also, try passing 1 and 2 to Xhandler()
}
catch(int i)
{
cout << "Caught an integer\n";
}
catch(char c)
{
cout << "Caught char\n";
}
catch(double d)

Dept of Computer Science And Applications, SJCET, Palai 208


MODULE 5 MCA-204 Object Oriented Programming and C++ ADMN 2009-‘10

{
cout << "Caught double\n";
}
cout << "end";
return 0;
}
In this program, the function Xhandler() may only throw integer, character,
and double exceptions. If it attempts to throw any other type of exception, an
abnormal program termination will occur. (That is, unexpected() will be called.) To
see an example of this, remove int from the list and retry the program. It is important
to understand that a function can be restricted only in what types of exceptions it
throws back to the try block that called it. That is, a try block within a function may
throw any type of exception so long as it is caught within that function. The restriction
applies only when throwing an exception outside of the function.
The following change to Xhandler() prevents it from throwing any exceptions.
// This function can throw NO exceptions!
void Xhandler(int test) throw()
{
/* The following statements no longer work. Instead,
they will cause an abnormal program termination. */
if(test==0) throw test;
if(test==1) throw 'a';
if(test==2) throw 123.23;
}
At the time of this writing, Microsoft's Visual C++ does not support the throw(
) clause for functions.

5.16.4.3 RETHROWING AN EXCEPTION


If you wish to rethrow an expression from within an exception handler, you
may do so by calling throw, by itself, with no exception. This causes the current
exception to be passed on to an outer try/catch sequence. The most likely reason for
doing so is to allow multiple handlers access to the exception. For example, perhaps
one exception handler manages one aspect of an exception and a second handler copes
with another. An exception can only be rethrown from within a catch block (or from
any function called from within that block). When you rethrow an exception, it will
not be recaught by the same catch statement. It will propagate outward to the next
catch statement. The following program illustrates rethrowing an exception, in this
case a char * exception.
// Example of "rethrowing" an exception.
#include <iostream>

Dept of Computer Science And Applications, SJCET, Palai 209


MODULE 5 MCA-204 Object Oriented Programming and C++ ADMN 2009-‘10

using namespace std;


void Xhandler()
{
try
{
throw "hello"; // throw a char *
}
catch(const char *)
{ // catch a char *
cout << "Caught char * inside Xhandler\n";
throw ; // rethrow char * out of function
}
}
int main()
{
cout << "Start\n";
try
{
Xhandler();
}
catch(const char *)
{
cout << "Caught char * inside main\n";
}
cout << "End";
return 0;
}
This program displays this output:
Start
Caught char * inside Xhandler
Caught char * inside main
End

5.16.5 UNDERSTANDING TERMINATE( ) AND UNEXPECTED( )


The terminate() and unexpected() are called when something goes wrong
during the exception handling process. These functions are supplied by the Standard
C++ library. Their prototypes are shown here:
void terminate( );
void unexpected( );

Dept of Computer Science And Applications, SJCET, Palai 210


MODULE 5 MCA-204 Object Oriented Programming and C++ ADMN 2009-‘10

These functions require the header <exception>. The terminate() function is


called whenever the exception handling subsystem fails to find a matching catch
statement for an exception. It is also called if your program attempts to rethrow an
exception when no exception was originally thrown. The terminate() function is also
called under various other, more obscure circumstances.
For example, such a circumstance could occur when, in the process of
unwinding the stack because of an exception, a destructor for an object being
destroyed throws an exception. In general, terminate() is the handler of last resort
when no other handlers for an exception are available. By default, terminate() calls
abort() .
The unexpected() function is called when a function attempts to throw an
exception that is not allowed by its throw list. By default, unexpected() calls
terminate() .

The uncaught_exception( ) Function


The C++ exception handling subsystem supplies one other function that you
may find useful: uncaught_exception() . Its prototype is shown here:
bool uncaught_exception( );
This function returns true if an exception has been thrown but not yet caught.
Once caught, the function returns false.

The exception and bad_exception Classes


When a function supplied by the C++ standard library throws an exception, it
will be an object derived from the base class exception. An object of the class
bad_exception can be thrown by the unexpected handler. These classes require the
header <exception>.

Dept of Computer Science And Applications, SJCET, Palai 211

You might also like