0% found this document useful (0 votes)
10 views46 pages

Oop Lect7

Uploaded by

fairykomal993
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
10 views46 pages

Oop Lect7

Uploaded by

fairykomal993
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 46

About Instructor

Mr. Fayez Afzaal


Lecturer
Department of Computer Science
COMSATS University Islamabad,
Sahiwal Campus, Pakistan
Email: [email protected]
Object-Oriented
Programming in C++

Lecture 7
Templates and Exceptions
Templates
 There are situations where same algorithm works for different data type.

 Suppose a function that returns the maximum of two numbers can work for different data
types

 Until now we have to write two separate function


• Int max(int, int);
• float max (float, float);

 Through generic programming we can have just one function to find max of int or float or
any other type of values

 In C++ generic programming is implemented using templates.

 Templates make it possible to use one function or class to handle many different data types.

 The template concept can be used in two different ways: with functions and with classes.
We’ll look at function templates first, then go on to class templates, and finally to
exceptions.
Function Templates
 Suppose you want to write a function that returns the absolute value of two numbers.

 The absolute value of 3 is 3, and the absolute value of –3 is also 3. Normally this function
would be written for a particular data type:

int abs(int n) //absolute value of ints


{
return (n<0) ? -n : n; //if n is negative, return -n
}
Here the function is defined to take an argument of type int and to return a value of this
same type. But now suppose you want to find the absolute value of a type long. You will
need to write a completely new function:
long abs(long n) //absolute value of longs
{
return (n<0) ? -n : n;
}
And again, for type float:
float abs(float n) //absolute value of floats
{
return (n<0) ? -n : n;
}
Function Templates
 The body of the function is written the same way in each case, but they are completely
different functions because they handle arguments and return values of different types.

 It’s true that in C++ these functions can all be overloaded to have the same name, but you
must nevertheless write a separate definition for each one.

 Rewriting the same function body over and over for different types is time-consuming and
wastes space in the listing. Also, if you find you’ve made an error in one such function, you’ll
need to remember to correct it in each function body.

 Failing to do this correctly is a good way to introduce inconsistencies into your program.

 It would be nice if there were a way to write such a function just once, and have it work for
many different data types. This is exactly what function templates do for you.
A Simple Function Template
 Our first example shows how to write our absolute-value function as a template, so that it will
work with any basic numerical type. This program defines a template version of abs() and
then, in main(), invokes this function with different data types to prove that it works.

// template used for absolute value function


#include <iostream>
using namespace std; //calls instantiate functions
template <class T> //function template cout << “\nabs(“ << int1 << “)=” << abs(int1);
T abs(T n) //abs(int)
{ cout << “\nabs(“ << int2 << “)=” << abs(int2);
return (n < 0) ? -n : n; //abs(int)
} cout << “\nabs(“ << lon1 << “)=” << abs(lon1);
int main() { //abs(long)
int int1 = 5; cout << “\nabs(“ << lon2 << “)=” << abs(lon2);
int int2 = -6; //abs(long)
long lon1 = 70000L; cout << “\nabs(“ << dub1 << “)=” << abs(dub1);
long lon2 = -80000L; //abs(double)
double dub1 = 9.95; cout << “\nabs(“ << dub2 << “)=” << abs(dub2);
double dub2 = -10.15; //abs(double)
cout << endl;
return 0;
}
Program Output
Here’s the output of the program:
abs(5)=5
abs(-6)=6
abs(70000)=70000
abs(-80000)=80000
abs(9.95)=9.95
abs(-10.15)=10.15

 As you can see, the abs() function now works with all three of the data types (int, long, and
double) that we use as arguments.

 Here’s how we specify the abs() function to work with multiple data types:

template <class T> //function template


T abs(T n)
{
return (n<0) ? -n : n;
}
This entire syntax, with a first line starting with the keyword template and the function definition
following, is called a function template.
Function Template Syntax
 The key innovation in function templates is to represent the data type used by the function
not as a specific type such as int, but by a name that can stand for any type.

 In the preceding function template, this name is T. (There’s nothing magic about this name;
it can be anything you want, like Type, or anyType, or FooBar.)

 The template keyword signals the compiler that we’re about to define a function template.
The keyword class might just as well be called type.

 The keyword class is used to define the name of general data type.

 As we’ve seen, you can define your own data types using classes, so there’s really no
distinction between types and classes. The variable following the keyword class (T in this
example) is called the template argument.
What the Compiler Does
 What does the compiler do when it sees the template keyword and the function definition
that follows it? Well, nothing right away.

 The function template itself doesn’t cause the compiler to generate any code. It can’t
generate code because it doesn’t know yet what data type the function will be working
with.

 It simply remembers the template for possible future use. Code generation doesn’t take
place until the function is actually called (invoked) by a statement within the program in the
statement
cout << “\nabs(“ << int << “)=” << abs(int1);

 When the compiler sees such a function call, it knows that the type to use is int, because
that’s the type of the argument int1.

 So it generates a specific version of the abs() function for type int, substituting int wherever it
sees the name T in the function template. This is called instantiating the function template,
and each instantiated version of the function is called a template function.
What the Compiler Does
 Similarly, the expression abs(lon1) causes the compiler to generate a version of abs() that
operates on type long and a call to this function, while the abs(dub1) call generates a
function that works on type double.

 Of course, the compiler is smart enough to generate only one version of abs() for each
data type.

 Thus, even though there are two calls to the int version of the function, the code for this
version appears only once in the executable code.

Simplifying the Listing

 Notice that the amount of RAM used by the program is the same whether we use the
template approach or actually write three separate functions. The template approaches
simply saves us from having to type three separate functions into the source file.

 This makes the listing shorter and easier to understand. Also, if we want to change the way
the function works, we need to make the change in only one place in the listing instead of
three places.

 The compiler decides how to compile the function based entirely on the data type used in
the function call’s argument (or arguments). The function’s return type doesn’t enter into
this decision.
Function Templates with Multiple
Arguments
 Let’s look at another example of a function template. This one takes three arguments: two
that are template arguments and one of a basic type.

 The purpose of this function is to search an array for a specific value. The function returns
the array index for that value if it finds it, or –1 if it can’t find it.

 The arguments are array, the value to search for, and the size of the array.

 In main() we define four different arrays of different types, and four values to search for. We
treat type char as a number. Then we call the template function once for each array.
Program example
// template used for function that finds number in array
#include <iostream>
using namespace std; int main()
{
//function returns index number of item, cout << “\n d in chrArray: index=” << find(chrArr, ch, 6);
or -1 if not found cout << “\n 6 in intArray: index=” << find(intArr, in, 6);
template <class atype> cout << “\n11 in lonArray: index=” << find(lonArr, lo, 6);
int find(atype array[], atype value, int size) cout << “\n 4 in dubArray: index=” << find(dubArr, db, 6
{ cout << endl;
for(int j=0; j<size; j++) return 0;
if(array[j]==value) }
return j; Output
return -1; d in chrArray: index=3
} 6 in intArray: index=-1
char chrArr[] = {‘a’, ‘b’, ‘c’, ‘d’, ‘e’, ‘f’}; //array 11 in lonArray: index=4
char ch = ‘d’; //value to find 4 in dubArray: index=-1
int intArr[] = {1, 3, 5, 9, 11, 13};
int in = 6;
long lonArr[] = {1L, 3L, 5L, 9L, 11L, 13L};
long lo = 11L;
double dubArr[] = {1.0, 3.0, 5.0, 9.0, 11.0, 13.0};
double db = 4.0;
Template Arguments Must Match
 When a template function is invoked, all instances of the same template argument must be of
the same type. For example, in find(), if the array name is of type int, the value to search for
must also be of type int. You can’t say

 int intarray[] = {1, 3, 5, 7}; //int array


float f1 = 5.0; //float value
int value = find(intarray, f1, 4); //uh, oh

 Because the compiler expects all instances of atype to be the same type. It can
generate a function

find(int[], int, int);

 But it can’t generate find(int[], float, int);

 Because the first and second arguments must be the same type.

Syntax Variation
Some programmers put the template keyword and the function declarator on the same line:
template<class atype> int find(atype array[], atype value, int size)
{ //function body }
More Than One Template Argument
 You can use more than one template argument in a function template. For example,
suppose you like the idea of the find() function template, but you aren’t sure how large an
array it might be applied to.

 If the array is too large then type long would be necessary for the array size, instead of
type int.

 On the other hand, you don’t want to use type long if you don’t need to. You want to
select the type of the array size, as well as the type of data stored, when you call the
function.
 To make this possible, you could make the array size into a template argument as well.
We’ll call it btype:

template <class atype, class btype>


btype find(atype array[], atype value, btype size)
{
for(btype j=0; j<size; j++) //note use of btype
if(array[j]==value)
return j;
return static_cast<btype>(-1);
}
Why Not Macros?
 Old-time C programmers may wonder why we don’t use macros to create different versions
of a function for different data types. For example, the abs() function could be defined as
#define abs(n) ( (n<0) ? (-n) : (n) )

 This has a similar effect to the class template because it performs a simple text substitution
and can thus work with any type.

 However, as we’ve noted before, macros aren’t much used in C++. There are several
problems with them.

 One is that macros don’t perform any type checking. There may be several arguments to
the macro that should be of the same type, but the compiler won’t check whether or not
they are. Also, the type of the value returned isn’t specified.

 In any case, macros are confined to functions that can be expressed in a single statement.
There are also other, more subtle, problems with macros. On the whole it’s best to avoid
them.
Class Templates
 The template concept can be extended to classes. Class templates are generally used for
data storage (container) classes. Stacks, queues and linked lists are examples of data-storage
classes.

 Limitation: However, the examples of these classes could store data of only a single basic type
class LongStack
class Stack
{
{
private:
private:
long st[MAX]; //array of longs
int st[MAX]; //array of ints
int top; //index number of top of stack
int top; //index number of top of stack
public:
public:
LongStack(); //constructor
Stack(); //constructor
void push(long var); //takes long as argument
void push(int var); //takes int as argument
long pop(); //returns long value
int pop(); //returns int value
};
};

 Similarly, we would need to create a new stack class for every data type we wanted to store.
It would be nice to be able to write a single class specification that would work for variables of
all types, instead of a single basic type. As you may have guessed, class templates allow us to
do this.
Class Template Example
// implements stack class as a template int main()
#include <iostream.h> {
using namespace std; Stack<float> s1; //s1 is object of class Stack<float>
const int MAX = 100; //size of array s1.push(1111.1F); //push 3 floats, pop 3 floats
template <class Type> s1.push(2222.2F);
class Stack s1.push(3333.3F);
{ cout << “1: “ << s1.pop() << endl;
private: cout << “2: “ << s1.pop() << endl;
Type st[MAX]; //stack: array of any type cout << “3: “ << s1.pop() << endl;
int top; //number of top of stack
public: Stack<long> s2; //s2 is object of class Stack<long>
Stack() //constructor
{ top = -1; } s2.push(123123123L); //push 3 longs, pop 3 longs
void push(Type var) //put number on stack s2.push(234234234L);
{ st[++top] = var; } s2.push(345345345L);
Type pop() //take number off stack cout << “1: “ << s2.pop() << endl;
{ return st[top--]; } cout << “2: “ << s2.pop() << endl;
}; cout << “3: “ << s2.pop() << endl;
return 0;
}
Class Template
 Here the class Stack is presented as a template class. The approach is similar to that used in
function templates. The template keyword and class Stack signal that the entire class will be a
template.

template <class Type>


class Stack
{
//data and member functions using template argument Type
};

Stack<float> s1;

 This creates an object, s1, a stack that stores numbers of type float. The compiler provides
space in memory for this object’s data, using type float wherever the template argument Type
appears in the class specification. It also provides space for the member functions

 Creating a Stack object that stores objects of a different type, as in


Stack<long> s2;
creates not only a different space for data, but also a new set of member functions that
operate on type long.
Class Name Depends on Context
 In the previous example, the member functions of the class template were all defined
within the class.

 If the member functions are defined externally (outside of the class specification), we
need a new syntax. The next program shows how this works

// member functions are defined outside the class template<class Type>


#include <iostream> Stack<Type>::Stack() //constructor
using namespace std; {
const int MAX = 100; top = -1;
template <class Type> }
class Stack template<class Type>
{ void Stack<Type>::push(Type var)
private: {
Type st[MAX]; //stack: array of any type st[++top] = var;
int top; //number of top of stack }
public: template<class Type>
Stack(); //constructor Type Stack<Type>::pop()
void push(Type var); //put number on stack {
Type pop(); //take number off stack return st[top--];
}; }
int main()
{
Stack<float> s1;

s1.push(1111.1F); //push 3 floats, pop 3 floats


s1.push(2222.2F);
s1.push(3333.3F);
cout << “1: “ << s1.pop() << endl;
cout << “2: “ << s1.pop() << endl;
cout << “3: “ << s1.pop() << endl;
Stack<long> s2; //s2 is object of class Stack<long>
s2.push(123123123L); //push 3 longs, pop 3 longs
s2.push(234234234L);
s2.push(345345345L);
cout << “1: “ << s2.pop() << endl;
cout << “2: “ << s2.pop() << endl;
cout << “3: “ << s2.pop() << endl;
return 0;
}
Program Working
 The expression template<class Type> must precede not only the class definition, but
each externally defined member function as well. Here’s how the push() function
looks:

template<class Type>
void Stack<Type>::push(Type var)
{
st[++top] = var;
}

 The name Stack<Type> is used to identify the class of which push() is a member.

 In a normal non-template member function the name Stack alone would suffice:

void Stack::push(int var) //Stack() as a non-template function


{
st[++top] = var;
}
Exceptions
 Exceptions are errors that occur at runtime (execution time)

 Exceptional handling provide a systematic, object-oriented approach to handling


errors generated by C++ classes

 They are caused by a wide variety of exceptional circumstance, such as running


out of memory, not being able to open a file, trying to initialize an object to an
impossible value, or using an out-of-bounds index

Exception Syntax

 Ordinarily the application’s calls to the class member functions cause no problems.

 Sometimes, however, the application makes a mistake, causing an error to be


detected in a member function.

 This member function then informs the application that an error has occurred.
When exceptions are used, this is called throwing an exception.
Exceptions..
 In the application we install a separate section of code to handle the error.

 This code is called an exception handler or catch block; it catches the exceptions thrown by
the member function.

 Any code in the application that uses objects of the class is enclosed in a try block. Errors
generated in the try block will be caught in the catch block. Code that doesn’t interact with
the class need not be in a try block

 The exception mechanism uses three new C++ keywords: throw, catch, and try. Also, we need
to create a new kind of entity called an exception class
Exception Mechanism
Program Skeleton
class AClass //a class int main() //application
{ {
public: try //try block
class AnError //exception class {
{ AClass obj1; //interact with AClass objects
}; obj1.Func(); //may cause error
void Func() //a member function }
{ catch(AClass::AnError) //exception handler
if( /* error condition */ ) { //(catch block)
throw AnError(); //throw exception //tell user about error, etc.
} }; } return 0; }

 We start with a class called AClass, which represents any class in which errors might
occur. An exception class, AnError, is specified in the public part of AClass. In AClass’s
member functions we check for errors. If we find one, we throw an exception, using the
keyword throw followed by the constructor for the error class:

 throw AnError(); //’throw’ followed by constructor for AnError class

 In the main() part of the program we enclose any statements that interact with AClass
in a try block. If any of these statements causes an error to be detected in an AClass
member function, an exception will be thrown and control will go to the catch block
that immediately follows the try block.
A Simple Exception Example
 Let’s look at a working program example that uses exceptions.

 The application program might attempt to push too many objects onto the stack, thus
exceeding the capacity of the array, or it might try to pop too many objects off the stack,
thus obtaining invalid data. In the program we use an exception to handle these two
errors.
Stack() //constructor
// demonstrates exceptions { top = -1; }
#include <iostream> void push(int var)
using namespace std; {
const int MAX = 3; //stack holds 3 integers if(top >= MAX-1) //if stack full,
class Stack throw Range(); //throw exception
{ st[++top] = var; //put number on stack
private: }
int st[MAX]; //array of integers int pop()
int top; //index of top of stack {
public: if(top < 0) //if stack empty,
class Range //exception class for Stack throw Range(); //throw exception
{ //note: empty class body return st[top--]; //take number off stack
}; }
};
A Simple Exception Example
int main() {
Stack s1;
try
{
s1.push(11);
s1.push(22);
s1.push(33);
// s1.push(44); //oops: stack full
cout << “1: “ << s1.pop() << endl;
cout << “2: “ << s1.pop() << endl;
cout << “3: “ << s1.pop() << endl;
cout << “4: “ << s1.pop() << endl; //oops: stack empty
}
catch(Stack::Range) //exception handler
{
cout << “Exception: Stack Full or Empty” << endl;
}
cout << “Arrive here after catch (or normal exit)” << endl;
return 0;
}
Program Working
 Note that we’ve made the stack small so that it’s easier to trigger an exception by pushing
too many items.

 Let’s examine the features of this program that deal with exceptions. There are four of them. In
the class specification there is an exception class. There are also statements that throw
exceptions.

 In the main() part of the program there is a block of code that may cause exceptions (the try
block), and a block of code that handles the exception (the catch block).

Specifying the Exception Class

 The program first specifies an exception class within the Stack class:
class Range
{ //note: empty class body
};
 Here the body of the class is empty, so objects of this class have no data and no member
functions.
 All we really need in this simple example is the class name, Range. This name is used to
connect a throw statement with a catch block. (The class body need not always be empty,
as we’ll see later.)
Program Working
Throwing an Exception

 In the Stack class an exception occurs if the application tries to pop a value when the
stack is empty or tries to push a value when the stack is full.

 To let the application know that it has made such a mistake when manipulating a Stack
object, the member functions of the Stack class check for these conditions using if
statements, and throw an exception if they occur.

 In the exception is thrown in two places, both using the statement


throw Range();

 The Range() part of this statement invokes the implicit constructor for the Range class,
which creates an object of this class. The throw part of the statement transfers program
control to the exception handler
Program Working
The try Block

 All the statements in main() that might cause this exception that is, statements that
manipulate Stack objects are enclosed in braces and preceded by the try keyword:
try
{
//code that operates on objects that might cause an exception
}

 This is simply part of the application’s normal code; it’s what you would need to write even if
you weren’t using exceptions.

 Not all the code in the program needs to be in a try block; just the code that interacts with
the Stack class. Also, there can be many try blocks in your program, so you can access
Stack objects from different places.
Program Working
The Exception Handler (Catch Block)

 The code that handles the exception is enclosed in braces, preceded by the catch keyword,
with the exception class name in parentheses. The exception class name must include the
class in which it is located. Here it’s Stack::Range.

catch(Stack::Range)
{
//code that handles the exception
}

 This construction is called the exception handler. It must immediately follow the try block. In
program the exception handler simply prints an error message to let the user know why the
program failed.

 Control “falls through” the bottom of the exception handler, so you can continue processing
at that point. Or the exception handler may transfer control elsewhere, or (often) terminate
the program.
Summary
Sequence of Events

 Let’s summarize the sequence of events when an exception occurs:

1. Code is executing normally outside a try block.


2. Control enters the try block.
3. A statement in the try block causes an error in a member function.
4. The member function throws an exception.
5. Control transfers to the exception handler (catch block) following the try block.
Multiple Exceptions
 You can design a class to throw as many exceptions as you want. To show how this works,
we’ll modify the previous program to throw separate exceptions for attempting to push data
on a full stack and attempting to pop data from an empty stack.

// demonstrates two exception handlers void push(int var) //put number on stack
#include <iostream> {
using namespace std; if(top >= MAX-1) //if stack full,
const int MAX = 3; //stack holds 3 integers throw Full(); //throw Full exception
class Stack st[++top] = var;
{ }
private: int pop() //take number off stack
int st[MAX]; //stack: array of integers {
int top; //index of top of stack if(top < 0) //if stack empty,
public: throw Empty(); //throw Empty exception
class Full { }; //exception class return st[top--];
class Empty { }; //exception class }
Stack() //constructor };
{ top = -1; }
Multiple Exceptions
int main()
catch(Stack::Full)
{
{
Stack s1;
cout << “Exception: Stack Full” << endl;
try
}
{
catch(Stack::Empty)
s1.push(11);
{
s1.push(22);
cout << “Exception: Stack Empty” <<
s1.push(33);
endl;
// s1.push(44); //oops: stack full
}
cout << “1: “ << s1.pop() << endl;
return 0;
cout << “2: “ << s1.pop() << endl;
}
cout << “3: “ << s1.pop() << endl;
cout << “4: “ << s1.pop() << endl; //oops: stack empty
}
Program Working
 we specify two exception classes:
class Full { };
class Empty { };

 The statement throw Full();


is executed if the application calls push() when the stack is already full, and throw Empty();
is executed if pop() is called when the stack is empty. A separate catch block is used for
each exception:
try
{ //code that operates on Stack objects }

catch(Stack::Full)
{ //code to handle Full exception }

catch(Stack::Empty)
{ //code to handle Empty exception }

 All the catch blocks used with a particular try block must immediately follow the try block. In
this case each catch block simply prints a message: “Stack Full” or “Stack Empty”. Only one
catch block is activated for a given exception. A group of catch blocks, or a catch ladder,
operates a little like a switch statement, with only the appropriate section of code being
executed. When an exception has been handled, control passes to the statement following
all the catch blocks.
Exceptions with the Distance Class
 Let’s look at another example of exceptions, this one applied to the infamous Distance class
from previous chapters.

 A Distance object has an integer value for feet and a floating-point value for inches. The
inches value should always be less than 12.0.

 A problem with this class in previous examples has been that it couldn’t protect itself if the
user initialized an object with an inches value of 12.0 or greater. This could lead to trouble
when the class tried to perform arithmetic, since the arithmetic routines (such as operator +())
assumed inches would be less than 12.0.

 Such impossible values could also be displayed, thus confounding the user with dimensions
like 7’–15”.

 Let’s rewrite the Distance class to use an exception to handle this error,
Program Example
// exceptions with Distance class void getdist() //get length from user
#include <iostream> {
using namespace std; cout << “\nEnter feet: “; cin >> feet;
class Distance //English Distance class cout << “Enter inches: “; cin >> inches;
{ if(inches >= 12.0) //if inches too big,
private: throw InchesEx(); //throw exception
int feet; }
float inches; void showdist() //display distance
public: {
class InchesEx { }; //exception class cout << feet << “\’-” << inches << ‘\”’; }
};
Distance() //constructor (no args)
{ feet = 0; inches = 0.0; }

Distance(int ft, float in) //constructor (two args)


{
if(in >= 12.0) //if inches too big,
throw InchesEx(); //throw exception
feet = ft;
inches = in;
}
Program Example
int main()
{
try
{
Distance dist1(17, 3.5); //2-arg constructor
Distance dist2; //no-arg constructor
dist2.getdist(); //get distance from user
//display distances
cout << “\ndist1 = “; dist1.showdist();
cout << “\ndist2 = “; dist2.showdist();
}
catch(Distance::InchesEx) //catch exceptions
{
cout << “\nInitialization error: “
“inches value is too large.”;
}
cout << endl;
return 0;
}
Exceptions with Arguments
 What happens if the application needs more information about what caused an exception?

 For instance, in the previous example, it might help the programmer to know what the bad
inches value actually was.

 Also, if the same exception is thrown by different member functions, as it is in previous


example, it would be nice to know which of the functions was the culprit.

 Is there a way to pass such information from the member function, where the exception is
thrown, to the application that catches it?

 You can answer this question if you remember that throwing an exception involves not only
transferring control to the handler, but also creating an object of the exception class by calling
its constructor. In previous example , for example, we create an object of type InchesEx when
we throw the exception with the statement

throw InchesEx();

 If we add data members to the exception class, we can initialize them when we create the
object. The exception handler can then retrieve the data from the object when it catches the
exception.
Program Example
// exceptions with arguments Distance() //constructor (no args)
#include <iostream> { feet = 0; inches = 0.0; }
#include <string> //--------------------------------------------------------------
using namespace std; Distance(int ft, float in) //constructor (two args)
{
class Distance //English Distance class if(in >= 12.0)
{ throw InchesEx(“2-arg constructor”, in);
private: feet = ft;
int feet; inches = in;
float inches; }
public: //--------------------------------------------------------------
void getdist() //get length from user
class InchesEx //exception class {
{ cout << “\nEnter feet: “; cin >> feet;
public: cout << “Enter inches: “; cin >> inches;
string origin; //for name of routine if(inches >= 12.0)
float iValue; //for faulty inches value throw InchesEx(“getdist() function”, inches);
InchesEx(string or, float in) //2-arg constructor }
{
origin = or; //store string
iValue = in; //store inches
}};
Program Example
void showdist() //display distance
{ cout << feet << “\’-” << inches << ‘\”’; }
};

int main()
{
try
{
Distance dist1(17, 3.5); //2-arg constructor
Distance dist2; //no-arg constructor
dist2.getdist(); //get value
//display distances
cout << “\ndist1 = “; dist1.showdist();
cout << “\ndist2 = “; dist2.showdist();
}
catch(Distance::InchesEx ix) //exception handler
{
cout << “\nInitialization error in “ << ix.origin
<< “.\n Inches value of “ << ix.iValue
<< “ is too large.”;
}
cout << endl;
return 0; }
Program Working
 There are three parts to the operation of passing data when throwing an exception: specifying
the data members and a constructor for the exception class, initializing this constructor when
we throw an exception, and accessing the object’s data when we catch the exception.

 Let’s look at these in turn.

Specifying Data in an Exception Class

It’s convenient to make the data in an exception class public so it can be accessed directly by
the exception handler. Here’s the specification for the new InchesEx exception class:
class InchesEx //exception class
{
public:
string origin; //for name of routine
float iValue; //for faulty inches value
InchesEx(string or, float in) //2-arg constructor
{
origin = or; //put string in object
iValue = in; //put inches value in object
}
};
Program Working
Initializing an Exception Object

 How do we initialize the data when we throw an exception? In the two-argument constructor
for the Stack class we say

 throw InchesEx(“2-arg constructor”, in);


And in the getdist() member function for Distance it’s throw InchesEx(“getdist() function”,
inches);

 When the exception is thrown, the handler will display the string and inches values. The string
will tell us which member function is throwing the exception, and the value of inches will report
the faulty inches value detected by the member function.

 This additional data will make it easier for the programmer or user to figure out what caused
the error.
Program Working
Extracting Data from the Exception Object

 How do we extract this data when we catch the exception? The simplest way is to make the
data a public part of the exception class, as we’ve done here. Then in the catch block we
can declare ix as the name of the exception object we’re catching. Using this name we can
refer to its data in the usual way, using the dot operator:

catch(Distance::InchesEx ix)
{//access ‘ix.origin’ and ‘ix.iValue’ directly }

 We can then display the value of ix.origin and ix.iValue. Here’s some interaction when the
user enters too large a value for inches:
Enter feet: 7
Enter inches: 13.5

Initialization error in getdist() function.


Inches value of 13.5 is too large.

 Similarly, if the programmer changes the definition of dist1 in main() to


Distance dist1(17, 22.25);
the resulting exception will cause this error message:
Initialization error in 2-arg constructor.
Inches value of 22.25 is too large.
The bad_alloc Class
 Standard C++ contains several built-in exception classes. The most commonly used is probably
bad_alloc, which is thrown if an error occurs when attempting to allocate memory with new.

 If you set up the appropriate try and catch blocks, you can make use of bad_alloc with very
little effort. Here’s a short example

// demonstrates bad_alloc exception catch(bad_alloc) //exception handler


#include <iostream> {
using namespace std; cout << “\nbad_alloc exception: can’t
allocate memory.\n”; }
int main() {
double *ptr[50]; //pointer to memory return 0;}
try
{
for(int i=0; i<50; i++){
ptr[i]= new double[500000000]; //
cout<<"Allocated 4* 50000000 doubles in ptr [ "<< i << " |\n";
}}

 Put all the statements that use new in a try block. The catch block that follows handles the
exception, often by displaying an error message and terminating the program.
Program Output

You might also like