Exceptions

What are exceptions?

Exceptions are a way to react to errors happening during the execution of a program. For example, a function may throw an exception when something goes wrong inside it (Example, it received an invalid argument). Some functions from the C++ Standard Library does throw exceptions in certain cases. It is possible to catch exceptions and react to them. If a thrown exception is not caught, the program is terminated.

Throwing exceptions

An exception is thrown by using the keyword throw followed by the value to throw (It usually is a value that represents the error that happened).

In the example above, the function divide throws an exception if the value of the second argument is 0. Below, the program is terminated after the function divide is called, because the exception thrown by it is not caught.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream> float divide(float a, float b) { if(b == 0) throw "Division by zero."; return a / b; } int main() { float c = divide(5, 0); /* The program is terminated here, because the exception thrown has not been caught. */ std::cout << "The program is terminated before that line may be printed." << std::endl; return 0; }

The message printed in the console says that the program has been terminated because a value of type const char* (A null-terminated string in that case) has been thrown, but not caught.

Catching exceptions

To catch exceptions, we must put the code that may throw the exceptions we want to catch, between braces {}, following the keyword try. After the braces {}, we must write the keyword catch followed by a variable type and name, between parentheses (). Then, we have to write, between braces {}, the code to execute if a value corresponding to the argument defined in the header of the catch statement, is thrown. If a catch structure catches an exception, the thrown value is given to it as argument. We may define many catch statements. After an exception has been caught, the execution of the program continues after the try/catch structure inside which the exception has been thrown.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <iostream> float divide(float a, float b) { if(b == 0) throw "Division by zero."; return a / b; } int main() { try{ float c = divide(5, 0); } // Instructions to execute if a value of type 'int' is thrown is the try statement above. catch(int val) { std::cout << "If a value of type 'int' is thrown inside the 'try' statement above, " << "it will be caught by this catch statement and " << "this string will be printed in the console." << std::endl; } /* The program is terminated here because the thrown exception is of type 'const char *' (Constant null-terminated string) and the only catch statement catches a value of type 'int'. */ std::cout << "The program it terminated before that line may be printed." << std::endl; return 0; }

The following example will catch the thrown value of type const char *:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <iostream> float divide(float a, float b) { if(b == 0) throw "Division by zero."; return a / b; } int main() { try{ float c = divide(5, 0); } catch(int val) { std::cout << "The following value of type 'int' has been caught: " << val << std::endl; } catch(const char* str) { std::cout << "The following string has been caugh: " << str << std::endl; } /* The exception thrown is caught, so the program continues. */ std::cout << "I am printed into the console." << std::endl; return 0; }

Catching everything

A catch statement which parameter is 3 dots (...), will catch any thrown value, but it will not have access to the value.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <iostream> int main() { try{ throw 9; } catch(...) { std::cout << "Catches everything. " << std::endl; } try{ throw "Null-terminated string."; } catch(...) { std::cout << "Catches everything. " << std::endl; } try{ int * ptr; throw ptr; } catch(...) { std::cout << "Catches everything. " << std::endl; } return 0; }

Throwing class objects

Throwing a string explaining the error that occurred works, but if all functions return strings, how can we define many different catch statements that react differently depending on the type of exception? A solution is to define a class for each type of error that may occur and throw objects of those classes. Then, we simply have to put an object of the type of error we want to catch as argument to the catch statements.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#include <iostream> class DivByZero { public: static std::string printErrorString() { std::cout << "Division by zero." << std::endl; } }; class NegativeNbIteration { public: static std::string printErrorString() { std::cout << "The number of iteration can not be negative." << std::endl; } }; float divide(float a, float b) { if(b == 0) throw DivByZero(); return a / b; } std::string repeat(std::string const& str, int nb) { if(nb < 0) throw NegativeNbIteration(); std::string repeatedStr; for(int c=0; c < nb; c++) repeatedStr += str; return repeatedStr; } int main() { try{ divide(16, 0); // Throws an exception. // That line is not reached. std::cout << repeat("Hello", -2); } catch(NegativeNbIteration const& nN) { // A negative number of iterations has been given to a function call. NegativeNbIteration::printErrorString(); } catch(DivByZero const& exc) { // The number zero has been given as right operand of a division. DivByZero::printErrorString(); } return 0; }

The class std::exception

The C++ Standard Library defines a class used to represent exceptions: std::exception. It is defined as a base class from which other exception classes inherit. The value of all exceptions thrown by the C++ Standard Library are objects that inherit from that class.

The class std::exception possesses a virtual method called what which returns a null-terminated string describing the exception.

Note that we can add the keyword noexcept at the end of the header of a function or method to enforce that it can not throw exceptions.

Let us define an exception that inherit from that class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <iostream> class DivByZero : public std::exception { public: virtual const char* what() const noexcept { return "Division by zero."; } }; float divide(float a, float b) { if(b == 0) throw DivByZero(); return a / b; } int main() { try{ divide(53, 0); } catch(DivByZero const& exc) { std::cout << "Exception: " << exc.what() << std::endl; } return 0; }

Note that since all the exceptions thrown by the C++ Standard Library are objects of a class that inherit from std::exception, a catch statement that takes a reference to an object of type std::exception would catch any exception thrown by the standard library.

Try structure inside a try structure

We can define try structures inside other try structures. Calling throw, without value, from a catch structure, throws the previously thrown value to the parent try structure.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream> int main() { try{ try{ throw 6; } catch(...) { throw; } } catch(int val) { std::cout << "Thrown value received: " << val << std::endl; } return 0; }