topic: templates and exception handlinguietkanpur.com/online_course/pa_cse_s204_1.pdftopic:...

33
University Institute Of Engineering and Technology Computer Science Department Subject: OOP using C++ Code: CSE-S204 Topic: Templates and Exception Handling 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. Generic Functions Ageneric 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. For example, the Quicksort sorting algorithm is the same whether it is applied to an array of integers or an array of floats. It is just that the type of the data being sorted is different. 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.

Upload: others

Post on 19-Aug-2020

3 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Topic: Templates and Exception Handlinguietkanpur.com/Online_Course/PA_CSE_S204_1.pdfTopic: Templates and Exception Handling The template is one of C++'s most sophisticated and high-powered

University Institute Of Engineering and Technology Computer Science Department

Subject: OOP using C++ Code: CSE-S204 Topic: Templates and Exception Handling 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. Generic Functions Ageneric 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. For example, the Quicksort sorting algorithm is the same whether it is applied to an array of integers or an array of floats. It is just that the type of the data being sorted is different. 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.

Page 2: Topic: Templates and Exception Handlinguietkanpur.com/Online_Course/PA_CSE_S204_1.pdfTopic: Templates and Exception Handling The template is one of C++'s most sophisticated and high-powered

// 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; } 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. Both terms will be used interchangeably in this book. 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.

Page 3: Topic: Templates and Exception Handlinguietkanpur.com/Online_Course/PA_CSE_S204_1.pdfTopic: Templates and Exception Handling The template is one of C++'s most sophisticated and high-powered

template <class X> void swapargs(X &a, X &b) { X temp; temp = a; a = b; 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. C++ 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( ). 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.

Page 4: Topic: Templates and Exception Handlinguietkanpur.com/Online_Course/PA_CSE_S204_1.pdfTopic: Templates and Exception Handling The template is one of C++'s most sophisticated and high-powered

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; 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() 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.

Page 5: Topic: Templates and Exception Handlinguietkanpur.com/Online_Course/PA_CSE_S204_1.pdfTopic: Templates and Exception Handling The template is one of C++'s most sophisticated and high-powered

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. 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. 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.

Page 6: Topic: Templates and Exception Handlinguietkanpur.com/Online_Course/PA_CSE_S204_1.pdfTopic: Templates and Exception Handling The template is one of C++'s most sophisticated and high-powered

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. 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; // 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. Generic Function Restrictions Generic functions are similar to overloaded functions except that they are more

Page 7: Topic: Templates and Exception Handlinguietkanpur.com/Online_Course/PA_CSE_S204_1.pdfTopic: Templates and Exception Handling The template is one of C++'s most sophisticated and high-powered

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"; } 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; } 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. Before moving on to generic classes, two examples of applying generic functions will be given. They illustrate how easy it is to take advantage of this powerful C++ feature. 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.

Page 8: Topic: Templates and Exception Handlinguietkanpur.com/Online_Course/PA_CSE_S204_1.pdfTopic: Templates and Exception Handling The template is one of C++'s most sophisticated and high-powered

// 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--) 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

Page 9: Topic: Templates and Exception Handlinguietkanpur.com/Online_Course/PA_CSE_S204_1.pdfTopic: Templates and Exception Handling The template is one of C++'s most sophisticated and high-powered

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 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. Compacting an Array Another function that benefits from being made into a template is called compact( ). This function compacts the elements in an array. It is not uncommon to want to remove elements from the middle of an array and then move the remaining elements down so that all unused elements are at the end. This sort of operation is the same for all types of arrays because it is independent of the type data actually being operated upon. The generic compact( ) function shown in the following program is called with a pointer to the first element in the array, the number of elements in the array, and the starting and ending indexes of the elements to be removed. The function then removes those elements and compacts the array. For the purposes of illustration, it also zeroes the unused elements at the end of the array that have been freed by the compaction. // A Generic array compaction function. #include <iostream> using namespace std; template <class X> void compact( X *items, // pointer to array to be compacted int count, // number of items in array int start, // starting index of compacted region int end) // ending index of compacted region { register int i; for(i=end+1; i<count; i++, start++) items[start] = items[i]; /* For the sake of illustration, the remainder of the array will be zeroed. */ for( ; start<count; start++) items[start] = (X) 0; } int main() { int nums[7] = {0, 1, 2, 3, 4, 5, 6}; char str[18] = "Generic Functions"; int i; cout << "Here is uncompacted integer array: "; for(i=0; i<7; i++) cout << nums[i] << ' '; cout << endl; cout << "Here is uncompacted string: "; for(i=0; i<18; i++)C++ cout << str[i] << ' '; cout << endl; compact(nums, 7, 2, 4); compact(str, 18, 6, 10);

Page 10: Topic: Templates and Exception Handlinguietkanpur.com/Online_Course/PA_CSE_S204_1.pdfTopic: Templates and Exception Handling The template is one of C++'s most sophisticated and high-powered

cout << "Here is compacted integer array: "; for(i=0; i<7; i++) cout << nums[i] << ' '; cout << endl; cout << "Here is compacted string: "; for(i=0; i<18; i++) cout << str[i] << ' '; cout << endl; return 0; } This program compacts two different types of arrays. One is an integer array, and the other is a string. However, the compact( ) function will work for any type of array. The output from this program in shown here. Here is uncompacted integer array: 0 1 2 3 4 5 6 Here is uncompacted string: G e n e r i c F u n c t i o n s Here is compacted integer array: 0 1 5 6 0 0 0 Here is compacted string: G e n e r i c t i o n s As the preceding examples illustrate, once you begin to think in terms of templates, many uses will naturally suggest themselves. As long as the underlying logic of a function is independent of the data, it can be made into a generic function. 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 C++ 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:

Page 11: Topic: Templates and Exception Handlinguietkanpur.com/Online_Course/PA_CSE_S204_1.pdfTopic: Templates and Exception Handling The template is one of C++'s most sophisticated and high-powered

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 { 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;

Page 12: Topic: Templates and Exception Handlinguietkanpur.com/Online_Course/PA_CSE_S204_1.pdfTopic: Templates and Exception Handling The template is one of C++'s most sophisticated and high-powered

s1.push('a'); s2.push('x'); s1.push('b'); s2.push('y'); s1.push('c'); 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 C++ 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]; }; 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

Page 13: Topic: Templates and Exception Handlinguietkanpur.com/Online_Course/PA_CSE_S204_1.pdfTopic: Templates and Exception Handling The template is one of C++'s most sophisticated and high-powered

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. 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. 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. Applying Template Classes: A Generic Array Class To illustrate the practical benefits of template classes, let's look at one way in which they are commonly applied. As you saw in Chapter 15, you can overload the [ ] operator. Doing so allows you to create your own array implementations, including "safe arrays" that provide run-time boundary checking. As you know, in C++, it is possible to overrun (or underrun) an array boundary at run time without generating

Page 14: Topic: Templates and Exception Handlinguietkanpur.com/Online_Course/PA_CSE_S204_1.pdfTopic: Templates and Exception Handling The template is one of C++'s most sophisticated and high-powered

a run-time error message. However, if you create a class that contains the array, and allow access to that array only through the overloaded [ ] subscripting operator, then you can intercept an out-of-range index. By combining operator overloading with a template class, it is possible to create a generic safe-array type that can be used for creating safe arrays of any data type. This type of array is shown in the following program: // A generic safe array example. #include <iostream> #include <cstdlib> using namespace std; const int SIZE = 10; template <class AType> class atype { AType a[SIZE]; public: atype() { register int i; for(i=0; i<SIZE; i++) a[i] = i; } AType &operator[](int i); }; // Provide range checking for atype. template <class AType> AType &atype<AType>::operator[](int i) { if(i<0 || i> SIZE-1) { cout << "\nIndex value of "; cout << i << " is out-of-bounds.\n"; exit(1); } return a[i]; } int main() { atype<int> intob; // integer array atype<double> doubleob; // double array int i; cout << "Integer array: "; for(i=0; i<SIZE; i++) intob[i] = i; for(i=0; i<SIZE; i++) cout << intob[i] << " "; cout << '\n'; cout << "Double array: "; for(i=0; i<SIZE; i++) doubleob[i] = (double) i/3; for(i=0; i<SIZE; i++) cout << doubleob[i] << " "; cout << '\n'; intob[12] = 100; // generates runtime error return 0; } This program implements a generic safe-array type and then demonstrates its use by creating an array of ints and an array of doubles. You should try creating other

Page 15: Topic: Templates and Exception Handlinguietkanpur.com/Online_Course/PA_CSE_S204_1.pdfTopic: Templates and Exception Handling The template is one of C++'s most sophisticated and high-powered

types of arrays. As this example shows, part of the power of generic classes is that they allow you to write the code once, debug it, and then apply it to any type of data without having to re-engineer it for each specific application. Using Non-Type Arguments with Generic Classes In the template specification for a generic class, you may also specify non-type arguments. That is, in a template specification you can specify what you would normally think of as a standard argument, such as an integer or a pointer. The syntax to accomplish this is essentially the same as for normal function parameters: simply include the type and name of the argument. For example, here is a better way to implement the safe-array class presented in the preceding section. It allows you to specify the size of the array. // Demonstrate non-type template arguments. #include <iostream> #include <cstdlib> using namespace std; // Here, int size is a non-type argument. template <class AType, int size> class atype { AType a[size]; // length of array is passed in size public: atype() { register int i; for(i=0; i<size; i++) a[i] = i; } AType &operator[](int i); }; // Provide range checking for atype. template <class AType, int size> AType &atype<AType, size>::operator[](int i) { if(i<0 || i> size-1) { cout << "\nIndex value of "; cout << i << " is out-of-bounds.\n"; exit(1); } return a[i]; } int main() { atype<int, 10> intob; // integer array of size 10 atype<double, 15> doubleob; // double array of size 15 int i; cout << "Integer array: "; for(i=0; i<10; i++) intob[i] = i; for(i=0; i<10; i++) cout << intob[i] << " "; cout << '\n'; cout << "Double array: "; for(i=0; i<15; i++) doubleob[i] = (double) i/3;

Page 16: Topic: Templates and Exception Handlinguietkanpur.com/Online_Course/PA_CSE_S204_1.pdfTopic: Templates and Exception Handling The template is one of C++'s most sophisticated and high-powered

for(i=0; i<15; i++) cout << doubleob[i] << " "; cout << '\n'; intob[12] = 100; // generates runtime error return 0; } Look carefully at the template specification for atype. Note that size is declared as an int. This parameter is then used within atype to declare the size of the array a. Even though size is depicted as a "variable" in the source code, its value is known at compile time. This allows it to be used to set the size of the array. size is also used in the bounds checking within the operator[ ]( ) function. Within main( ), notice how the integer and floating-point arrays are created. The second parameter specifies the size of each array. Non-type parameters are restricted to integers, pointers, or references. Other types, such as float, are not allowed. The arguments that you pass to a non-type parameter must consist of either an integer constant, or a pointer or reference to a global function or object. Thus, non-type parameters should themselves be thought of as constants, since their values cannot be changed. For example, inside operator[ ]( ), the following statement is not allowed. size = 10; // Error Since non-type parameters are treated as constants, they can be used to set the dimension of an array, which is a significant, practical benefit. As the safe-array example illustrates, the use of non-type parameters greatly expands the utility of template classes. Although the information contained in the non-type argument must be known at compile-time, this restriction is mild compared with the power offered by non-type parameters. Using Default Arguments with Template Classes A template class can have a default argument associated with a generic type. For example, template <class X=int> class myclass { //... Here, the type int will be used if no other type is specified when an object of type myclass is instantiated. It is also permissible for non-type arguments to take default arguments. The default value is used when no explicit value is specified when the class is instantiated. Default arguments for non-type parameters are specified using the same syntax as default arguments for function parameters. Here is another version of the safe-array class that uses default arguments for both the type of data and the size of the array. // Demonstrate default template arguments. #include <iostream> #include <cstdlib> using namespace std; // Here, AType defaults to int and size defaults to 10. template <class AType=int, int size=10> class atype { AType a[size]; // size of array is passed in size public: atype() { register int i;

Page 17: Topic: Templates and Exception Handlinguietkanpur.com/Online_Course/PA_CSE_S204_1.pdfTopic: Templates and Exception Handling The template is one of C++'s most sophisticated and high-powered

for(i=0; i<size; i++) a[i] = i; } AType &operator[](int i); }; // Provide range checking for atype. template <class AType, int size> AType &atype<AType, size>::operator[](int i) { if(i<0 || i> size-1) { cout << "\nIndex value of "; cout << i << " is out-of-bounds.\n"; exit(1); } return a[i]; } int main() { atype<int, 100> intarray; // integer array, size 100 atype<double> doublearray; // double array, default size atype<> defarray; // default to int array of size 10 int i; cout << "int array: "; for(i=0; i<100; i++) intarray[i] = i; for(i=0; i<100; i++) cout << intarray[i] << " "; cout << '\n'; cout << "double array: "; for(i=0; i<10; i++) doublearray[i] = (double) i/3; for(i=0; i<10; i++) cout << doublearray[i] << " "; cout << '\n'; cout << "defarray array: "; for(i=0; i<10; i++) defarray[i] = i; for(i=0; i<10; i++) cout << defarray[i] << " "; cout << '\n'; return 0; } Pay close attention to this line: template <class AType=int, int size=10> class atype { Here, AType defaults to type int, and size defaults to 10. As the program illustrates, atype objects can be created three ways: ■ explicitly specifying both the type and size of the array ■ explicitly specifying the type, but letting the size default to 10 ■ letting the type default to int and the size default to 10 C++ The use of default arguments—especially default types—adds versatility to your template classes. You can provide a default for the type of data most commonly used while still allowing the user of your classes to specialize them as needed. Explicit Class Specializations

Page 18: Topic: Templates and Exception Handlinguietkanpur.com/Online_Course/PA_CSE_S204_1.pdfTopic: Templates and Exception Handling The template is one of C++'s most sophisticated and high-powered

As with template functions, you can create an explicit specialization of a generic class. To do so, use the template<> construct, which works the same as it does for explicit function specializations. For example: // Demonstrate class specialization. #include <iostream> using namespace std; template <class T> class myclass { T x; public: myclass(T a) { cout << "Inside generic myclass\n"; x = a; } T getx() { return x; } }; // Explicit specialization for int. template <> class myclass<int> { int x; public: myclass(int a) { cout << "Inside myclass<int> specialization\n"; x = a * a; } int getx() { return x; } }; int main() { myclass<double> d(10.1); cout << "double: " << d.getx() << "\n\n"; myclass<int> i(5); cout << "int: " << i.getx() << "\n"; return 0; } This program displays the following output: Inside generic myclass double: 10.1 Inside myclass<int> specialization int: 25 In the program, pay close attention to this line: template <> class myclass<int> { It tells the compiler that an explicit integer specialization of myclass is being created. This same general syntax is used for any type of class specialization. Explicit class specialization expands the utility of generic classes because it lets you easily handle one or two special cases while allowing all others to be automatically processed by the compiler. Of course, if you find that you are creating too many specializations, you are probably better off not using a template class in the first place. The typename and export Keywords

Page 19: Topic: Templates and Exception Handlinguietkanpur.com/Online_Course/PA_CSE_S204_1.pdfTopic: Templates and Exception Handling The template is one of C++'s most sophisticated and high-powered

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. 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. When first shown in Chapter 11, 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 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++. C++

Page 20: Topic: Templates and Exception Handlinguietkanpur.com/Online_Course/PA_CSE_S204_1.pdfTopic: Templates and Exception Handling The template is one of C++'s most sophisticated and high-powered

Exception Handling This chapter discusses the exception handling subsystem. 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.

Page 21: Topic: Templates and Exception Handlinguietkanpur.com/Online_Course/PA_CSE_S204_1.pdfTopic: Templates and Exception Handling The template is one of C++'s most sophisticated and high-powered

Exception Handling Fundamentals 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 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 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, as

Page 22: Topic: Templates and Exception Handlinguietkanpur.com/Online_Course/PA_CSE_S204_1.pdfTopic: Templates and Exception Handling The template is one of C++'s most sophisticated and high-powered

described later in this chapter. 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"; C++ 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, 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

Page 23: Topic: Templates and Exception Handlinguietkanpur.com/Online_Course/PA_CSE_S204_1.pdfTopic: Templates and Exception Handling The template is one of C++'s most sophisticated and high-powered

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. (Of course, the precise message describing abnormal termination will vary from compiler to compiler.) 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; } 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

Page 24: Topic: Templates and Exception Handlinguietkanpur.com/Online_Course/PA_CSE_S204_1.pdfTopic: Templates and Exception Handling The template is one of C++'s most sophisticated and high-powered

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) { cout << "Caught Exception #: " << i << '\n'; } } int main() { cout << "Start\n"; Xhandler(1); 492 C + + : T h e C o m p l e t e R e f e r e n c e 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() {

Page 25: Topic: Templates and Exception Handlinguietkanpur.com/Online_Course/PA_CSE_S204_1.pdfTopic: Templates and Exception Handling The template is one of C++'s most sophisticated and high-powered

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"; } 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. 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; } }; C++ int main() { int i; try { cout << "Enter a positive number: "; cin >> i; if(i<0)

Page 26: Topic: Templates and Exception Handlinguietkanpur.com/Online_Course/PA_CSE_S204_1.pdfTopic: Templates and Exception Handling The template is one of C++'s most sophisticated and high-powered

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. 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'; } 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;

Page 27: Topic: Templates and Exception Handlinguietkanpur.com/Online_Course/PA_CSE_S204_1.pdfTopic: Templates and Exception Handling The template is one of C++'s most sophisticated and high-powered

} 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. Handling Derived-Class Exceptions You need to be careful how you order your catch statements when trying to catch exception types that involve base and derived classes because a catch clause for a base class will also match any class derived from that base. Thus, if you want to catch exceptions of both a base class type and a derived class type, put the derived class first in the catch sequence. If you don't do this, the base class catch will also catch all derived classes. For example, consider the following program. // Catching derived classes. #include <iostream> using namespace std; class B { }; class D: public B { }; int main() { D derived; try { throw derived; } catch(B b) { cout << "Caught a base class.\n"; } catch(D d) { cout << "This won't execute.\n"; } return 0; } Here, because derived is an object that has B as a base class, it will be caught by the first catch clause and the second clause will never execute. Some compilers will flag this condition with a warning message. Others may issue an error. Either way, to fix this condition, reverse the order of the catch clauses. Exception Handling Options

Page 28: Topic: Templates and Exception Handlinguietkanpur.com/Online_Course/PA_CSE_S204_1.pdfTopic: Templates and Exception Handling The template is one of C++'s most sophisticated and high-powered

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. 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 } 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> using namespace std; void Xhandler(int test) {

Page 29: Topic: Templates and Exception Handlinguietkanpur.com/Online_Course/PA_CSE_S204_1.pdfTopic: Templates and Exception Handling The template is one of C++'s most sophisticated and high-powered

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. 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 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. However, you can specify your own unexpected handler if you like, as described later in this chapter.

Page 30: Topic: Templates and Exception Handlinguietkanpur.com/Online_Course/PA_CSE_S204_1.pdfTopic: Templates and Exception Handling The template is one of C++'s most sophisticated and high-powered

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) { 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; }

Page 31: Topic: Templates and Exception Handlinguietkanpur.com/Online_Course/PA_CSE_S204_1.pdfTopic: Templates and Exception Handling The template is one of C++'s most sophisticated and high-powered

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> 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 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>.

Page 32: Topic: Templates and Exception Handlinguietkanpur.com/Online_Course/PA_CSE_S204_1.pdfTopic: Templates and Exception Handling The template is one of C++'s most sophisticated and high-powered

Applying Exception Handling Exception handling is designed to provide a structured means by which your program can handle abnormal events. This implies that the error handler must do something rational when an error occurs. For example, consider the following simple program. It inputs two numbers and divides the first by the second. It uses exception handling to manage a divide-by-zero error. #include <iostream> using namespace std; void divide(double a, double b); int main() { double i, j; do { cout << "Enter numerator (0 to stop): "; cin >> i; cout << "Enter denominator: "; cin >> j; divide(i, j); } while(i != 0); return 0; } void divide(double a, double b) { try { if(!b) throw b; // check for divide-by-zero cout << "Result: " << a/b << endl; } catch (double b) { cout << "Can't divide by zero.\n"; } } While the preceding program is a very simple example, it does illustrate the essential nature of exception handling. Since division by zero is illegal, the program cannot continue if a zero is entered for the second number. In this case, the exception is handled by not performing the division (which would have caused abnormal program termination) and notifying the user of the error. The program then reprompts the user for two more numbers. Thus, the error has been handled in an orderly fashion and the user may continue on with the program. The same basic concepts will apply to more complex applications of exception handling. Exception handling is especially useful for exiting from a deeply nested set of routines when a catastrophic error occurs. In this regard, C++'s exception handling is designed to replace the rather clumsy C-based setjmp( ) and longjmp( ) functions. Remember, the key point about using exception handling is to provide an orderly way of handling errors. This means rectifying the situation, if possible.

Page 33: Topic: Templates and Exception Handlinguietkanpur.com/Online_Course/PA_CSE_S204_1.pdfTopic: Templates and Exception Handling The template is one of C++'s most sophisticated and high-powered