UNIT-5
Templates & Exception handling
A C++ template is a powerful feature added to C++. It allows you to define the generic classes and generic functions and thus provides support for generic programming. Generic programming is a technique where generic types are used as parameters in algorithms so that they can work for a variety of data types.
Templates can be represented in two ways:
- Function templates
- Class templates
Fig 1 - Templates
Function Templates:
We can define a template for a function. For example, if we have an add() function, we can create versions of the add function for adding the int, float or double type values.
Class Template:
We can define a template for a class. For example, a class template can be created for the array class that can accept the array of various types such as int array, float array or double array.
- Generic functions use the concept of a function template. Generic functions define a set of operations that can be applied to the various types of data.
- The type of the data that the function will operate on depends on the type of the data passed as a parameter.
- For example, Quick sorting algorithm is implemented using a generic function, it can be implemented to an array of integers or array of floats.
- A Generic function is created by using the keyword template. The template defines what function will do.
- template < class Ttype> ret_type func_name(parameter_list)
- {
- // body of function.
- }
Where Ttype: It is a placeholder name for a data type used by the function. It is used within the function definition. It is only a placeholder that the compiler will automatically replace this placeholder with the actual data type.
class: A class keyword is used to specify a generic type in a template declaration.
Let's see a simple example of a function template:
- #include <iostream>
- using namespace std;
- template<class T> T add(T &a,T &b)
- {
- T result = a+b;
- return result;
- }
- int main()
- {
- int i =2;
- int j =3;
- float m = 2.3;
- float n = 1.2;
- cout<<"Addition of i and j is :"<<add(i,j);
- cout<<'\n';
- cout<<"Addition of m and n is :"<<add(m,n);
- return 0;
- }
Output:
Addition of i and j is :5
Addition of m and n is :3.5
In the above example, we create the function template which can perform the addition operation on any type either it can be integer, float or double.
Function Templates with Multiple Parameters
We can use more than one generic type in the template function by using the comma to separate the list.
- template<class T1, class T2,.....>
- return_type function_name (arguments of type T1, T2....)
- {
- // body of function.
- }
In the above syntax, we have seen that the template function can accept any number of arguments of a different type.
Let's see a simple example:
- #include <iostream>
- using namespace std;
- template<class X,class Y> void fun(X a,Y b)
- {
- std::cout << "Value of a is : " <<a<< std::endl;
- std::cout << "Value of b is : " <<b<< std::endl;
- }
- int main()
- {
- fun(15,12.3);
- return 0;
- }
Output:
Value of a is : 15
Value of b is : 12.3
In the above example, we use two generic types in the template function, i.e., X and Y.
Overloading a Function Template
We can overload the generic function means that the overloaded template functions can differ in the parameter list.
Let's understand this through a simple example:
- #include <iostream>
- using namespace std;
- template<class X> void fun(X a)
- {
- std::cout << "Value of a is : " <<a<< std::endl;
- }
- template<class X,class Y> void fun(X b ,Y c)
- {
- std::cout << "Value of b is : " <<b<< std::endl;
- std::cout << "Value of c is : " <<c<< std::endl;
- }
- int main()
- {
- fun(10);
- fun(20,30.5);
- return 0;
- }
Output:
Value of a is : 10
Value of b is : 20
Value of c is : 30.5
In the above example, template of fun() function is overloaded.
Restrictions of Generic Functions
Generic functions perform the same operation for all the versions of a function except the data type differs. Let's see a simple example of an overloaded function which cannot be replaced by the generic function as both the functions have different functionalities.
Let's understand this through a simple example:
- #include <iostream>
- using namespace std;
- void fun(double a)
- {
- cout<<"value of a is : "<<a<<'\n';
- }
- void fun(int b)
- {
- if(b%2==0)
- {
- cout<<"Number is even";
- }
- else
- {
- cout<<"Number is odd";
- }
- }
- int main()
- {
- fun(4.6);
- fun(6);
- return 0;
- }
Output:
value of a is : 4.6
Number is even
In the above example, we overload the ordinary functions. We cannot overload the generic functions as both the functions have different functionalities. First one is displaying the value and the second one determines whether the number is even or not.
Class Template can also be defined similarly to the Function Template. When a class uses the concept of Template, then the class is known as generic class.
- template<class Ttype>
- class class_name
- {
- .
- .
- }
Ttype is a placeholder name which will be determined when the class is instantiated. We can define more than one generic data type using a comma-separated list. The Ttype can be used inside the class body.
Now, we create an instance of a class
- class_name<type> ob;
where class_name: It is the name of the class.
type: It is the type of the data that the class is operating on.
ob: It is the name of the object.
Let's see a simple example:
- #include <iostream>
- using namespace std;
- template<class T>
- class A
- {
- public:
- T num1 = 5;
- T num2 = 6;
- void add()
- {
- std::cout << "Addition of num1 and num2 : " << num1+num2<<std::endl;
- }
- };
- int main()
- {
- A<int> d;
- d.add();
- return 0;
- }
Output:
Addition of num1 and num2 : 11
In the above example, we create a template for class A. Inside the main() method, we create the instance of class A named as, 'd'.
CLASS TEMPLATE WITH MULTIPLE PARAMETERS
We can use more than one generic data type in a class template, and each generic data type is separated by the comma.
- template<class T1, class T2, ......>
- class class_name
- {
- // Body of the class.
- }
Let's see a simple example when class template contains two generic data types.
- #include <iostream>
- using namespace std;
- template<class T1, class T2>
- class A
- {
- T1 a;
- T2 b;
- public:
- A(T1 x,T2 y)
- {
- a = x;
- b = y;
- }
- void display()
- {
- std::cout << "Values of a and b are : " << a<<" ,"<<b<<std::endl;
- }
- };
- int main()
- {
- A<int,float> d(5,6.5);
- d.display();
- return 0;
- }
Output:
Values of a and b are : 5,6.5
The template can contain multiple arguments, and we can also use the non-type arguments In addition to the type T argument, we can also use other types of arguments such as strings, function names, constant expression and built-in types. Let' s see the following example:
- template<class T, int size>
- class array
- {
- T arr[size]; // automatic array initialization.
- };
In the above case, the nontype template argument is size and therefore, template supplies the size of the array as an argument.
Arguments are specified when the objects of a class are created:
- array<int, 15> t1; // array of 15 integers.
- array<float, 10> t2; // array of 10 floats.
- array<char, 4> t3; // array of 4 chars.
Let's see a simple example of nontype template arguments.
- #include <iostream>
- using namespace std;
- template<class T, int size>
- class A
- {
- public:
- T arr[size];
- void insert()
- {
- int i =1;
- for (int j=0;j<size;j++)
- {
- arr[j] = i;
- i++;
- }
- }
- void display()
- {
- for(int i=0;i<size;i++)
- {
- std::cout << arr[i] << " ";
- }
- }
- };
- int main()
- {
- A<int,10> t1;
- t1.insert();
- t1.display();
- return 0;
- }
Output:
1 2 3 4 5 6 7 8 9 10
In the above example, the class template is created which contains the nontype template argument, i.e., size. It is specified when the object of class 'A' is created.
Points to Remember
- C++ supports a powerful feature known as a template to implement the concept of generic programming.
- A template allows us to create a family of classes or family of functions to handle different data types.
- Template classes and functions eliminate the code duplication of different data types and thus makes the development easier and faster.
- Multiple parameters can be used in both class and function template.
- Template functions can also be overloaded.
- We can also use nontype arguments such as built-in or derived data types as template arguments.
Key takeaway
A C++ template is a powerful feature added to C++. It allows you to define the generic classes and generic functions and thus provides support for generic programming. Generic programming is a technique where generic types are used as parameters in algorithms so that they can work for a variety of data types.
Generics is the idea to allow type (Integer, String, … etc and user-defined types) to be a parameter to methods, classes and interfaces. For example, classes like an array, map, etc, which can be used using generics very efficiently. We can use them for any type.
The method of Generic Programming is implemented to increase the efficiency of the code. Generic Programming enables the programmer to write a general algorithm which will work with all data types. It eliminates the need to create different algorithms if the data type is an integer, string or a character.
The advantages of Generic Programming are
- Code Reusability
- Avoid Function Overloading
- Once written it can be used for multiple times and cases.
Generics can be implemented in C++ using Templates. Template is a simple and yet very powerful tool in C++. The simple idea is to pass data type as a parameter so that we don’t need to write the same code for different data types. For example, a software company may need sort() for different data types. Rather than writing and maintaining the multiple codes, we can write one sort() and pass data type as a parameter.
Generic Functions using Template:
We write a generic function that can be used for different data types. Examples of function templates are sort(), max(), min(), printArray()
#include <iostream> using namespace std;
// One function works for all data types. // This would work even for user defined types // if operator '>' is overloaded template <typename T>
T myMax(T x, T y) { return (x > y) ? x : y; }
int main() {
// Call myMax for int cout << myMax<int>(3, 7) << endl;
// call myMax for double cout << myMax<double>(3.0, 7.0) << endl;
// call myMax for char cout << myMax<char>('g', 'e') << endl;
return 0; } |
Output:
7
7
g
Like function templates, class templates are useful when a class defines something that is independent of data type. Can be useful for classes like LinkedList, binary tree, Stack, Queue, Array, etc.
Following is a simple example of template Array class.
#include <iostream> using namespace std;
template <typename T> class Array { private: T* ptr; int size;
public: Array(T arr[], int s); void print(); };
template <typename T> Array<T>::Array(T arr[], int s) { ptr = new T[s]; size = s; for (int i = 0; i < size; i++) ptr[i] = arr[i]; }
template <typename T> void Array<T>::print() { for (int i = 0; i < size; i++) cout << " " << *(ptr + i); cout << endl; }
int main() { int arr[5] = { 1, 2, 3, 4, 5 }; Array<int> a(arr, 5); a.print(); return 0; } |
Output:
1 2 3 4 5
Working with multi-type Generics:
We can pass more than one data types as arguments to templates. The following example demonstrates the same.
#include <iostream> using namespace std;
template <class T, class U> class A { T x; U y;
public: A() { cout << "Constructor Called" << endl; } };
int main() { A<char, char> a; A<int, double> b; return 0; } |
Output:
Constructor Called
Constructor Called
Key takeaway
Generics is the idea to allow type (Integer, String, … etc and user-defined types) to be a parameter to methods, classes and interfaces. For example, classes like an array, map, etc, which can be used using generics very efficiently. We can use them for any type.
The method of Generic Programming is implemented to increase the efficiency of the code. Generic Programming enables the programmer to write a general algorithm which will work with all data types. It eliminates the need to create different algorithms if the data type is an integer, string or a character.
The C++ keyword export was originally designed to eliminate the need to include a template validation (either by providing the definition in the header file or by including the implementation file). However, only a few compilers ever supported this capability, such as Comeau C++ and Sun Studio, and the general concensus was that it was not worth the trouble.
Because of that, the C++11 standardization committee has voted to remove the export feature from the language. Assuming this meets final approval, export will remain a reserved word but it will no longer have any meaning in the standard.
If you are working with a compiler that supports the export keyword, it will probably continue to support the keyword via some sort of compiler option or extension until its users migrate away from it. If you already have code that uses export, you can use a fairly simple discipline to allow your code to easily migrate if/when your compiler stops supporting it entirely. Just define your template header-files like this:
// File Foo.h
#ifdef USE_EXPORT_KEYWORD
export
#endif
template<typename T>
class Foo {
...
};
#ifndef USE_EXPORT_KEYWORD
#include "Foo.cpp"
#endif
And define your non-inline functions in a source-file like this:
// File Foo.cpp
#ifdef USE_EXPORT_KEYWORD
export
#endif
template<typename T> ...
Then compile with -DUSE_EXPORT_KEYWORD, or whatever equivalent compiler option lets you set a preprocessor symbol like USE_COMPILER_KEYWORD, and if/when your compiler removes support for export, just remove that compiler option.
Key takeaway
The C++ keyword export was originally designed to eliminate the need to include a template validation (either by providing the definition in the header file or by including the implementation file). However, only a few compilers ever supported this capability, such as Comeau C++ and Sun Studio, and the general concensus was that it was not worth the trouble.
Because of that, the C++11 standardization committee has voted to remove the export feature from the language. Assuming this meets final approval, export will remain a reserved word but it will no longer have any meaning in the standard.
Hope you have already understood the concept of C++ Template which we have discussed earlier. The C++ STL (Standard Template Library) is a powerful set of C++ template classes to provide general-purpose classes and functions with templates that implement many popular and commonly used algorithms and data structures like vectors, lists, queues, and stacks.
At the core of the C++ Standard Template Library are following three well-structured components −
Sr.No | Component & Description |
1 | Containers Containers are used to manage collections of objects of a certain kind. There are several different types of containers like deque, list, vector, map etc. |
2 | Algorithms Algorithms act on containers. They provide the means by which you will perform initialization, sorting, searching, and transforming of the contents of containers. |
3 | Iterators Iterators are used to step through the elements of collections of objects. These collections may be containers or subsets of containers. |
We will discuss about all the three C++ STL components in next chapter while discussing C++ Standard Library. For now, keep in mind that all the three components have a rich set of pre-defined functions which help us in doing complicated tasks in very easy fashion.
Let us take the following program that demonstrates the vector container (a C++ Standard Template) which is similar to an array with an exception that it automatically handles its own storage requirements in case it grows −
#include <iostream>
#include <vector>
using namespace std;
int main() {
// create a vector to store int
vector<int> vec;
int i;
// display the original size of vec
cout << "vector size = " << vec.size() << endl;
// push 5 values into the vector
for(i = 0; i < 5; i++) {
vec.push_back(i);
}
// display extended size of vec
cout << "extended vector size = " << vec.size() << endl;
// access 5 values from the vector
for(i = 0; i < 5; i++) {
cout << "value of vec [" << i << "] = " << vec[i] << endl;
}
// use iterator to access the values
vector<int>::iterator v = vec.begin();
while( v != vec.end()) {
cout << "value of v = " << *v << endl;
v++;
}
return 0;
}
When the above code is compiled and executed, it produces the following result −
vector size = 0
extended vector size = 5
value of vec [0] = 0
value of vec [1] = 1
value of vec [2] = 2
value of vec [3] = 3
value of vec [4] = 4
value of v = 0
value of v = 1
value of v = 2
value of v = 3
value of v = 4
Here are following points to be noted related to various functions we used in the above example −
- The push_back( ) member function inserts value at the end of the vector, expanding its size as needed.
- The size( ) function displays the size of the vector.
- The function begin( ) returns an iterator to the start of the vector.
- The function end( ) returns an iterator to the end of the vector.
Containers in C++ STL (Standard Template Library)
A container is a holder object that stores a collection of other objects (its elements). They are implemented as class templates, which allows a great flexibility in the types supported as elements.
The container manages the storage space for its elements and provides member functions to access them, either directly or through iterators (reference objects with similar properties to pointers).
Sequence containers
Sequence containers implement data structures which can be accessed sequentially.
- Array : Static contiguous array (class template)
- Vector : Dynamic contiguous array (class template)
- Dequeue : Double-ended queue (class template)
- Forward List : Singly-linked list (class template)
- List : Doubly-linked list (class template)
Associative containers
Associative containers implement sorted data structures that can be quickly searched (O(log n) complexity).
- Set : Collection of unique keys, sorted by keys
(class template) - Map: Collection of key-value pairs, sorted by keys, keys are unique (class template).
- Multiset : Collection of keys, sorted by keys (class template)
- Multimap : Collection of key-value pairs, sorted by keys
(class template)
Unordered associative containers
Unordered associative containers implement unsorted (hashed) data structures that can be quickly searched (O(1) amortized, O(n) worst-case complexity).
- unordered_set: Collection of unique keys, hashed by keys. (class template)
- unordered_map: Collection of key-value pairs, hashed by keys, keys are unique. (class template)
- unordered_multiset: Collection of keys, hashed by keys (class template)
- unordered_multimap: Collection of key-value pairs, hashed by keys (class template)
Container adaptors
Container adaptors provide a different interface for sequential containers.
- Stack: Adapts a container to provide stack (LIFO data structure) (class template).
- Queue: Adapts a container to provide queue (FIFO data structure) (class template).
- Priority Queue: Adapts a container to provide priority queue (class template).
Fig 2 – Adaptive containers
Fig 3 - Flowchart of Adaptive Containers and Unordered Containers
Flowchart of Sequence containers and ordered containers
For all those who aspire to excel in competitive programming, only having a knowledge about containers of STL is of less use till one is not aware what all STL has to offer.
STL has an ocean of algorithms, for all < algorithm > library functions :
Some of the most used algorithms on vectors and most useful one’s in Competitive Programming are mentioned as follows :
Non-Manipulating Algorithms
- sort(first_iterator, last_iterator) – To sort the given vector.
- reverse(first_iterator, last_iterator) – To reverse a vector.
- *max_element (first_iterator, last_iterator) – To find the maximum element of a vector.
- *min_element (first_iterator, last_iterator) – To find the minimum element of a vector.
- accumulate(first_iterator, last_iterator, initial value of sum) – Does the summation of vector elements
// A C++ program to demonstrate working of sort(), // reverse() #include <algorithm> #include <iostream> #include <vector> #include <numeric> //For accumulate operation using namespace std;
int main() { // Initializing vector with array values int arr[] = {10, 20, 5, 23 ,42 , 15}; int n = sizeof(arr)/sizeof(arr[0]); vector<int> vect(arr, arr+n);
cout << "Vector is: "; for (int i=0; i<n; i++) cout << vect[i] << " ";
// Sorting the Vector in Ascending order sort(vect.begin(), vect.end());
cout << "\nVector after sorting is: "; for (int i=0; i<n; i++) cout << vect[i] << " ";
// Reversing the Vector reverse(vect.begin(), vect.end());
cout << "\nVector after reversing is: "; for (int i=0; i<6; i++) cout << vect[i] << " ";
cout << "\nMaximum element of vector is: "; cout << *max_element(vect.begin(), vect.end());
cout << "\nMinimum element of vector is: "; cout << *min_element(vect.begin(), vect.end());
// Starting the summation from 0 cout << "\nThe summation of vector elements is: "; cout << accumulate(vect.begin(), vect.end(), 0);
return 0; } |
Output
Vector is: 10 20 5 23 42 15
Vector after sorting is: 5 10 15 20 23 42
Vector after reversing is: 42 23 20 15 10 5
Maximum element of vector is: 42
Minimum element of vector is: 5
The summation of vector elements is: 115
6.count(first_iterator, last_iterator,x) – To count the occurrences of x in vector.
7. find(first_iterator, last_iterator, x) – Points to last address of vector ((name_of_vector).end()) if element is not present in vector.
// C++ program to demonstrate working of count() // and find() #include <algorithm> #include <iostream> #include <vector> using namespace std;
int main() { // Initializing vector with array values int arr[] = {10, 20, 5, 23 ,42, 20, 15}; int n = sizeof(arr)/sizeof(arr[0]); vector<int> vect(arr, arr+n);
cout << "Occurrences of 20 in vector : ";
// Counts the occurrences of 20 from 1st to // last element cout << count(vect.begin(), vect.end(), 20);
// find() returns iterator to last address if // element not present find(vect.begin(), vect.end(),5) != vect.end()? cout << "\nElement found": cout << "\nElement not found";
return 0; } |
Output
Occurrences of 20 in vector : 2
Element found
8. binary search(first_iterator, last_iterator, x) – Tests whether x exists in sorted vector or not.
9. lower_bound(first_iterator, last_iterator, x) – returns an iterator pointing to the first element in the range [first,last) which has a value not less than ‘x’.
10. upper_bound(first_iterator, last_iterator, x) – returns an iterator pointing to the first element in the range [first,last) which has a value greater than ‘x’.
// C++ program to demonstrate working of lower_bound() // and upper_bound(). #include <algorithm> #include <iostream> #include <vector> using namespace std;
int main() { // Initializing vector with array values int arr[] = {5, 10, 15, 20, 20, 23, 42, 45}; int n = sizeof(arr)/sizeof(arr[0]); vector<int> vect(arr, arr+n);
// Sort the array to make sure that lower_bound() // and upper_bound() work. sort(vect.begin(), vect.end());
// Returns the first occurrence of 20 auto q = lower_bound(vect.begin(), vect.end(), 20);
// Returns the last occurrence of 20 auto p = upper_bound(vect.begin(), vect.end(), 20);
cout << "The lower bound is at position: "; cout << q-vect.begin() << endl;
cout << "The upper bound is at position: "; cout << p-vect.begin() << endl;
return 0; } |
Output
The lower bound is at position: 3
The upper bound is at position: 5
Some Manipulating Algorithms
- arr.erase(position to be deleted) – This erases selected element in vector and shifts and resizes the vector elements accordingly.
- arr.erase(unique(arr.begin(),arr.end()),arr.end()) – This erases the duplicate occurrences in sorted vector in a single line.
// C++ program to demonstrate working of erase() #include <algorithm> #include <iostream> #include <vector> using namespace std;
int main() { // Initializing vector with array values int arr[] = {5, 10, 15, 20, 20, 23, 42, 45}; int n = sizeof(arr)/sizeof(arr[0]); vector<int> vect(arr, arr+n);
cout << "Vector is :"; for (int i=0; i<6; i++) cout << vect[i]<<" ";
// Delete second element of vector vect.erase(vect.begin()+1);
cout << "\nVector after erasing the element: "; for (int i=0; i<5; i++) cout << vect[i] << " ";
// sorting to enable use of unique() sort(vect.begin(), vect.end());
cout << "\nVector before removing duplicate " " occurrences: "; for (int i=0; i<5; i++) cout << vect[i] << " ";
// Deletes the duplicate occurrences vect.erase(unique(vect.begin(),vect.end()),vect.end());
cout << "\nVector after deleting duplicates: "; for (int i=0; i< vect.size(); i++) cout << vect[i] << " ";
return 0; } |
Output
Vector is :5 10 15 20 20 23
Vector after erasing the element: 5 15 20 20 23
Vector before removing duplicate occurrences: 5 15 20 20 23
Vector after deleting duplicates: 5 15 20 23 42 45
3. next_permutation(first_iterator, last_iterator) – This modified the vector to its next permutation.
4. prev_permutation(first_iterator, last_iterator) – This modified the vector to its previous permutation.
// C++ program to demonstrate working // of next_permutation() // and prev_permutation() #include <algorithm> #include <iostream> #include <vector> using namespace std;
int main() { // Initializing vector with array values int arr[] = {5, 10, 15, 20, 20, 23, 42, 45}; int n = sizeof(arr)/sizeof(arr[0]); vector<int> vect(arr, arr+n);
cout << "Given Vector is:\n"; for (int i=0; i<n; i++) cout << vect[i] << " ";
// modifies vector to its next permutation order next_permutation(vect.begin(), vect.end()); cout << "\nVector after performing next permutation:\n"; for (int i=0; i<n; i++) cout << vect[i] << " ";
prev_permutation(vect.begin(), vect.end()); cout << "\nVector after performing prev permutation:\n"; for (int i=0; i<n; i++) cout << vect[i] << " ";
return 0; } |
Output
Given Vector is:
5 10 15 20 20 23 42 45
Vector after performing next permutation:
5 10 15 20 20 23 45 42
Vector after performing prev permutation:
5 10 15 20 20 23 42 45
5. distance(first_iterator,desired_position) – It returns the distance of desired position from the first iterator.This function is very useful while finding the index.
// C++ program to demonstrate working of distance() #include <algorithm> #include <iostream> #include <vector> using namespace std;
int main() { // Initializing vector with array values int arr[] = {5, 10, 15, 20, 20, 23, 42, 45}; int n = sizeof(arr)/sizeof(arr[0]); vector<int> vect(arr, arr+n);
// Return distance of first to maximum element cout << "Distance between first to max element: "; cout << distance(vect.begin(), max_element(vect.begin(), vect.end())); return 0; } |
Output
Distance between first to max element: 7
Key takeaway
Hope you have already understood the concept of C++ Template which we have discussed earlier. The C++ STL (Standard Template Library) is a powerful set of C++ template classes to provide general-purpose classes and functions with templates that implement many popular and commonly used algorithms and data structures like vectors, lists, queues, and stacks.
An exception is a problem that arises during the execution of a program. A C++ exception is a response to an exceptional circumstance that arises while a program is running, such as an attempt to divide by zero.
Exceptions provide a way to transfer control from one part of a program to another. C++ exception handling is built upon three keywords: try, catch, and throw.
- throw − A program throws an exception when a problem shows up. This is done using a throw keyword.
- catch − A program catches an exception with an exception handler at the place in a program where you want to handle the problem. The catch keyword indicates the catching of an exception.
- try − A try block identifies a block of code for which particular exceptions will be activated. It's followed by one or more catch blocks.
Assuming a block will raise an exception, a method catches an exception using a combination of the try and catch keywords. A try/catch block is placed around the code that might generate an exception. Code within a try/catch block is referred to as protected code, and the syntax for using try/catch as follows −
try {
// protected code
} catch( ExceptionName e1 ) {
// catch block
} catch( ExceptionName e2 ) {
// catch block
} catch( ExceptionName eN ) {
// catch block
}
You can list down multiple catch statements to catch different type of exceptions in case your try block raises more than one exception in different situations.
Exceptions can be thrown anywhere within a code block using throw statement. The operand of the throw statement determines a type for the exception and can be any expression and the type of the result of the expression determines the type of exception thrown.
Following is an example of throwing an exception when dividing by zero condition occurs −
double division(int a, int b) {
if( b == 0 ) {
throw "Division by zero condition!";
}
return (a/b);
}
The catch block following the try block catches any exception. You can specify what type of exception you want to catch and this is determined by the exception declaration that appears in parentheses following the keyword catch.
try {
// protected code
} catch( ExceptionName e ) {
// code to handle ExceptionName exception
}
Above code will catch an exception of ExceptionName type. If you want to specify that a catch block should handle any type of exception that is thrown in a try block, you must put an ellipsis, ..., between the parentheses enclosing the exception declaration as follows −
try {
// protected code
} catch(...) {
// code to handle any exception
}
The following is an example, which throws a division by zero exception and we catch it in catch block.
#include <iostream>
using namespace std;
double division(int a, int b) {
if( b == 0 ) {
throw "Division by zero condition!";
}
return (a/b);
}
int main () {
int x = 50;
int y = 0;
double z = 0;
try {
z = division(x, y);
cout << z << endl;
} catch (const char* msg) {
cerr << msg << endl;
}
return 0;
}
Because we are raising an exception of type const char*, so while catching this exception, we have to use const char* in catch block. If we compile and run above code, this would produce the following result −
Division by zero condition!
C++ provides a list of standard exceptions defined in <exception> which we can use in our programs. These are arranged in a parent-child class hierarchy shown below −
Fig 4 – Standard exception
Here is the small description of each exception mentioned in the above hierarchy −
Sr.No | Exception & Description |
1 | std::exception An exception and parent class of all the standard C++ exceptions. |
2 | std::bad_alloc This can be thrown by new. |
3 | std::bad_cast This can be thrown by dynamic_cast. |
4 | std::bad_exception This is useful device to handle unexpected exceptions in a C++ program. |
5 | std::bad_typeid This can be thrown by typeid. |
6 | std::logic_error An exception that theoretically can be detected by reading the code. |
7 | std::domain_error This is an exception thrown when a mathematically invalid domain is used. |
8 | std::invalid_argument This is thrown due to invalid arguments. |
9 | std::length_error This is thrown when a too big std::string is created. |
10 | std::out_of_range This can be thrown by the 'at' method, for example a std::vector and std::bitset<>::operator[](). |
11 | std::runtime_error An exception that theoretically cannot be detected by reading the code. |
12 | std::overflow_error This is thrown if a mathematical overflow occurs. |
13 | std::range_error This is occurred when you try to store a value which is out of range. |
14 | std::underflow_error This is thrown if a mathematical underflow occurs. |
You can define your own exceptions by inheriting and overriding exception class functionality. Following is the example, which shows how you can use std::exception class to implement your own exception in standard way −
#include <iostream>
#include <exception>
using namespace std;
struct MyException : public exception {
const char * what () const throw () {
return "C++ Exception";
}
};
int main() {
try {
throw MyException();
} catch(MyException& e) {
std::cout << "MyException caught" << std::endl;
std::cout << e.what() << std::endl;
} catch(std::exception& e) {
//Other errors
}
}
This would produce the following result −
MyException caught
C++ Exception
Here, what() is a public method provided by exception class and it has been overridden by all the child exception classes. This returns the cause of an exception.
Key takeaway
An exception is a problem that arises during the execution of a program. A C++ exception is a response to an exceptional circumstance that arises while a program is running, such as an attempt to divide by zero.
Exceptions provide a way to transfer control from one part of a program to another. C++ exception handling is built upon three keywords: try, catch, and throw.
When executing C++ code, different errors can occur: coding errors made by the programmer, errors due to wrong input, or other unforeseeable things.
When an error occurs, C++ will normally stop and generate an error message. The technical term for this is: C++ will throw an exception (throw an error).
Exception handling in C++ consist of three keywords: try, throw and catch:
The try statement allows you to define a block of code to be tested for errors while it is being executed.
The throw keyword throws an exception when a problem is detected, which lets us create a custom error.
The catch statement allows you to define a block of code to be executed, if an error occurs in the try block.
The try and catch keywords come in pairs:
try {
// Block of code to try
throw exception; // Throw an exception when a problem arise
}
catch () {
// Block of code to handle errors
}
Consider the following example:
try {
int age = 15;
if (age >= 18) {
cout << "Access granted - you are old enough.";
} else {
throw (age);
}
}
catch (int myNum) {
cout << "Access denied - You must be at least 18 years old.\n";
cout << "Age is: " << myNum;
}
Example explained
We use the try block to test some code: If the age variable is less than 18, we will throw an exception, and handle it in our catch block.
In the catch block, we catch the error and do something about it. The catch statement takes a parameter: in our example we use an int variable (myNum) (because we are throwing an exception of int type in the try block (age)), to output the value of age.
If no error occurs (e.g. if age is 20 instead of 15, meaning it will be be greater than 18), the catch block is skipped:
int age = 20;
You can also use the throw keyword to output a reference number, like a custom error number/code for organizing purposes:
try {
int age = 15;
if (age >= 18) {
cout << "Access granted - you are old enough.";
} else {
throw 505;
}
}
catch (int myNum) {
cout << "Access denied - You must be at least 18 years old.\n";
cout << "Error number: " << myNum;
}
Handle Any Type of Exceptions (...)
If you do not know the throw type used in the try block, you can use the "three dots" syntax (...) inside the catch block, which will handle any type of exception:
try {
int age = 15;
if (age >= 18) {
cout << "Access granted - you are old enough.";
} else {
throw 505;
}
}
catch (...) {
cout << "Access denied - You must be at least 18 years old.\n";
}
Key takeaway
When executing C++ code, different errors can occur: coding errors made by the programmer, errors due to wrong input, or other unforeseeable things.
When an error occurs, C++ will normally stop and generate an error message. The technical term for this is: C++ will throw an exception (throw an error).
REFERENCE
1 Object-Oriented Programming with C++ by E. Balaguruswamy. (Tata McGraw-Hill) 6th Edition and onwards
2. Object oriented Programming with C++- by SouravSahay (Oxford) 2ndedition