You can use C++ standard exception classes to create exception objects and throw exceptions.
The catch block parameter in the preceding examples is the int type. A class type is often more useful, because an object can contain more information that you want to throw to a catch block. C++ provides a number of standard classes that can be used for creating exception objects. These classes are shown in Figure 16.1.
Figure 16.1 You can use standard library classes to create exception objects.
The root class in this hierarchy is exception (defined in header <exception>). It contains the virtual function what() that returns an exception object’s error message.
The runtime_error class (defined in header <stdexcept>) is a base class for several standard exception classes that describes runtime errors. Class overflow_error describes an arithmetic overflow—i.e., a value is too large to be stored. Class underflow_error describes an arithmetic underflow—i.e., a value is too small to be stored.
The logic_error class (defined in header <stdexcept>) is a base class for several standard exception classes that describes logic errors. Class invalid_argument indicates that an invalid argument has been passed to a function. Class length_error indicates that an object’s length has exceeded the maximum allowed length. Class out_of_range indicates that a value has exceeded its allowed range.
Classes bad_alloc, bad_cast, bad_typeid, and bad_exception describe the exceptions thrown by C++ operators. For example, a bad_alloc exception is thrown by the new operator if the memory cannot be allocated. A bad_cast exception is thrown by the dynamic_cast operator as the result of a failed cast to a reference type. A bad_typeid exception is thrown by the typeid operator when the operand for typeid is a NULL pointer. The bad_exception class is used in the exception-specification of a function, which will be discussed in Section 16.9.
These classes are used by some functions in the C++ standard library to throw exceptions. You also can use these classes to throw exceptions in your programs. Listing 16.5 rewrites Listing 16.4, QuotientWithFunction.cpp, by throwing a runtime_error.
Listing 16.5 QuotientThrowRuntimeError.cpp
1 #include <iostream>
2 #include <stdexcept>
3 using namespace std;
4
5 int quotient(int number1, int number2)
6 {
7 if (number2 == 0)
8 throw runtime_error(“Divisor cannot be zero”);
9
10 return number1 / number2;
11 }
12
13 int main()
14 {
15 // Read two integers
16 cout << “Enter two integers: “;
17 int number1, number2;
18 cin >> number1 >> number2;
19
20 try
21 {
22 int result = quotient(number1, number2);
23 cout << number1 << ” / ” << number2 << ” is ”
24 << result << endl;
25 }
26 catch (runtime_error& ex)
27 {
28 cout << ex.what() << endl;
29 }
30
31 cout << “Execution continues …” << endl;
32
33 return 0;
34 }
The quotient function in Listing 16.4 throws an int value, but the function in this program throws a runtime_error object (line 8). You can create a runtime_error object by passing a string that describes the exception.
The catch block catches a runtime_error exception and invokes the what function to return a string description of the exception (line 28).
Listing 16.6 shows an example of handling the bad_alloc exception.
Listing 16.6 BadAllocExceptionDemo.cpp
1 #include <iostream>
2 using namespace std;
3
4 int main()
5 {
6 try
7 {
8 for (int i = 1; i <= 100; i++)
9 {
10 new int[70000000];
11 cout << i << ” arrays have been created” << endl;
12 }
13 }
14 catch (bad_alloc& ex)
15 {
16 cout << “Exception: ” << ex.what() << endl;
17 }
18
19 return 0;
20 }
The output shows that the program creates six arrays before it fails on the seventh new operator. When it fails, a bad_alloc exception is thrown and caught in the catch block, which displays the message returned from ex.what().
Listing 16.7 shows an example of handling the bad_cast exception.
Listing 16.7 BadCastExceptionDemo.cpp
1 #include <typeinfo>
2 #include “DerivedCircleFromAbstractGeometricObject.h”
3 #include “DerivedRectangleFromAbstractGeometricObject.h”
4 #include <iostream>
5 using namespace std;
6
7 int main()
8 {
9 try
10 {
11 Rectangle r(3, 4);
12 Circle& c = dynamic_cast<Circle&>(r);
13 }
14 catch (bad_cast& ex)
15 {
16 cout << “Exception: ” << ex.what() << endl;
17 }
18
19 return 0;
20 }
Dynamic casting was introduced in Section 15.10, “Casting: static_cast versus dynamic_cast.” In line 12, a reference of a Rectangle object is cast to a Circle reference type, which is illegal, and a bad_cast exception is thrown. The exception is caught in the catch block in line 14.
Listing 16.8 shows an example of throwing and handling an invalid_argument exception.
Listing 16.8 InvalidArgumentExceptionDemo.cpp
1 #include <iostream>
2 #include <stdexcept>
3 using namespace std;
4
5 double getArea(double radius)
6 {
7 if (radius < 0)
8 throw invalid_argument(“Radius cannot be negative”);
9
10 return radius * radius * 3.14159;
11 }
12
13 int main()
14 {
15 // Prompt the user to enter radius
16 cout << “Enter radius: “;
17 double radius;
18 cin >> radius;
19
20 try
21 {
22 double result = getArea(radius);
23 cout << “The area is ” << result << endl;
24 }
25 catch (exception& ex)
26 {
27 cout << ex.what() << endl;
28 }
29
30 cout << “Execution continues …” << endl;
31
32 return 0;
33 }
In the sample output, the program prompts the user to enter radius, 5 and -5. Invoking getArea(-5) (line 22) causes an invalid_argument exception to be thrown (line 8). This exception is caught in the catch block in line 25. Note that the catch-block parameter type exception is a base class for invalid_argument. So, it can catch an invalid_argument.
Source: Liang Y. Daniel (2013), Introduction to programming with C++, Pearson; 3rd edition.