Now let’s see how you can handle problems by using the exception mechanism. 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. Handling an exception has three components:
• Throwing an exception
• Catching an exception with a handler
• Using a try
block
A program throws an exception when a problem shows up. For example, you can modify hmean()
in Listing 15.7 to throw an exception instead of call the abort()
function. A throw statement, in essence, is a jump; that is, it tells a program to jump to statements at another location. The throw
keyword indicates the throwing of an exception. It’s followed by a value, such as a character string or an object, that indicates the nature of the exception.
A program catches an exception with an exception handler at the place in the program where you want to handle the problem. The catch
keyword indicates the catching of an exception. A handler begins with the keyword catch
, followed by a type declaration (in parentheses) that indicates the type of exception to which it responds. That, in turn, is followed by a brace-enclosed block of code that indicates the actions to take. The catch
keyword, along with the exception type, serves as a label that identifies the point in a program to which execution should jump when an exception is thrown. An exception handler is also called a catch
block.
A try
block identifies a block of code for which particular exceptions will be activated. It’s followed by one or more catch
blocks. The try
block itself is indicated by the keyword try
, followed by a brace-enclosed block of code indicating the code for which exceptions will be noticed.
The easiest way to see how these three elements fit together is to look at a short example, such as the one in Listing 15.9.
// error3.cpp -- using an exception
#include <iostream>
double hmean(double a, double b);
int main()
{
double x, y, z;
std::cout << "Enter two numbers: ";
while (std::cin >> x >> y)
{
try { // start of try block
z = hmean(x,y);
} // end of try block
catch (const char * s) // start of exception handler
{
std::cout << s << std::endl;
std::cout << "Enter a new pair of numbers: ";
continue;
} // end of handler
std::cout << "Harmonic mean of " << x << " and " << y
<< " is " << z << std::endl;
std::cout << "Enter next set of numbers <q to quit>: ";
}
std::cout << "Bye!
";
return 0;
}
double hmean(double a, double b)
{
if (a == -b)
throw "bad hmean() arguments: a = -b not allowed";
return 2.0 * a * b / (a + b);
}
Here’s a sample run of the program in Listing 15.9:
Enter two numbers: 3 6
Harmonic mean of 3 and 6 is 4
Enter next set of numbers <q to quit>: 10 -10
bad hmean() arguments: a = -b not allowed
Enter a new pair of numbers: 1 19
Harmonic mean of 1 and 19 is 1.9
Enter next set of numbers <q to quit>: q
Bye!
The try
block in Listing 15.9 looks like this:
try { // start of try block
z = hmean(x,y);
} // end of try block
If any statement in this block leads to an exception being thrown, the catch
blocks after this block will handle the exception. If the program calls hmean()
somewhere else outside this (and any other) try
block, it won’t have the opportunity to handle an exception.
Throwing an exception looks like this:
if (a == -b)
throw "bad hmean() arguments: a = -b not allowed";
In this case, the thrown exception is the string "bad hmean() arguments: a = -b not allowed"
. The exception type can be a string, as in this case, or other C++ types. A class type is the usual choice, as later examples in this chapter illustrate.
Executing the throw
is a bit like executing a return statement in that it terminates function execution. However, instead of returning control to the calling program, a throw
causes a program to back up through the sequence of current function calls until it finds the function that contains the try
block. In Listing 15.9, that function is the same as the calling function. Soon you’ll see an example involving backing up more than one function. Meanwhile, in this case, the throw
passes program control back to main()
. There, the program looks for an exception handler (following the try
block) that matches the type of exception thrown.
The handler, or catch
block, looks like this:
catch (char * s) // start of exception handler
{
std::cout << s << std::endl;
sdt::cout << "Enter a new pair of numbers: ";
continue;
} // end of handler
The catch
block looks a bit like a function definition, but it’s not. The keyword catch
identifies this as a handler, and char * s
means that this handler matches a thrown exception that is a string. This declaration of s
acts much like a function argument definition in that a matching thrown exception is assigned to s
. Also, if an exception does match this handler, the program executes the code within the braces.
If a program completes executing statements in a try
block without any exceptions being thrown, it skips the catch
block or blocks after the try
block and goes to the first statement following the handlers. So when the sample run of the program in Listing 15.9 processes the values 3
and 6
, program execution goes directly to the output statement and reports the result.
Let’s trace through the events in the sample run that occur after the values 10
and -10
are passed to the hmean()
function. The if
test causes hmean()
to throw an exception. This terminates execution of hmean()
. Searching back, the program determines that hmean()
was called from within a try
block in main()
. It then looks for a catch
block with a type that matches the exception type. The single catch
block present has a char *
parameter, so it does match. Detecting the match, the program assigns the string "bad hmean() arguments: a = -b not allowed"
to the variable s
. Next, the program executes the code in the handler. First, it prints s
, which is the caught exception. Then it prints instructions to the user to enter new data. Finally, it executes a continue
statement, which causes the program to skip the rest of the while
loop and jump to its beginning again. The fact that the continue
statement takes the program to the beginning of the loop illustrates the fact that handler statements are part of the loop and that the catch
line acts like a label directing program flow (see Figure 15.2).
You might wonder what happens if a function throws an exception and there’s no try
block or else no matching handler. By default, the program eventually calls the abort()
function, but you can modify that behavior. We’ll return to this topic later in this chapter.
3.145.87.161