basic exception handling

In the previous lesson on basic exception handling, we explained how throw, try, and catch work together to enable exception handling. This lesson is dedicated to showing more examples of exception handling at work in various cases.

Exceptions within functions

In all of the examples in the previous lesson, the throw statements were placed directly within a try block. If this were a necessity, exception handling would be of limited use.

One of the most useful properties of exception handling is that the throw statements do NOT have to be placed directly inside a try block due to the way exceptions propagate when thrown. This allows us to use exception handling in a much more modular fashion. We’ll demonstrate this by rewriting the square root program from the previous lesson to use a modular function.

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
#include "math.h" // for sqrt() function
using namespace std;
 
// A modular square root function
double MySqrt( double dX)
{
     // If the user entered a negative number, this is an error condition
     if (dX < 0.0)
         throw "Can not take sqrt of negative number" ; // throw exception of type char*
 
     return sqrt (dX);
}
 
int main()
{
     cout << "Enter a number: " ;
     double dX;
     cin >> dX;
 
     try // Look for exceptions that occur within try block and route to attached catch block(s)
     {
         cout << "The sqrt of " << dX << " is " << MySqrt(dX) << endl;
     }
     catch ( char * strException) // catch exceptions of type char*
     {
         cerr << "Error: " << strException << endl;
     }
}

In this program, we’ve taken the code that checks for an exception and calculates the square root and put it inside a modular function called MySqrt(). We’ve then called this MySqrt() function from inside a try block. Let’s verify that it still works as expected:

Enter a number: -4
Error: Can not take sqrt of negative number

It does!

The most interesting part of this program is the MySqrt() function, which potentially raises an exception. However, you will note that this exception is not inside of a try block! This essentially means MySqrt is willing to say, “Hey, there’s a problem!”, but is unwilling to handle the problem itself. It is, in essence, delegating that responsibility to its caller (the equivalent of how using a return code passes the responsibility of handling an error back to a function’s caller).

Let’s revisit for a moment what happens when an exception is raised. First, the program looks to see if the exception can be handled immediately (which means it was thrown inside a try block). If not, it immediately terminates the current function and checks to see if the caller will handle the exception. If not, it terminates the caller and checks the caller’s caller. Each function is terminated in sequence until a handler for the exception is found, or until main() terminates. This process is called unwinding the stack (see the lesson on the stack and the heap if you need a refresher on what the call stack is).

Now, let’s take a detailed look at how that applies to this program when MySqrt(-4) is called and an exception is raised.

First, the program checks to see if we’re immediately inside a try block within the function. In this case, we are not. Then, the stack begins to unwind. First, MySqrt() terminates, and control returns to main(). The program now checks to see if we’re inside a try block. We are, and there’s a char* handler, so the exception is handled by the try block within main(). To summarize, MySqrt() raised the exception, but the try/catch block in main() was the one who captured and handled the exception.

At this point, some of you are probably wondering why it’s a good idea to pass errors back to the caller. Why not just make MySqrt() handle it’s own error? The problem is that different applications may want to handle errors in different ways. A console application may want to print a text message. A windows application may want to pop up an error dialog. In one application, this may be a fatal error, and in another application it may not. By passing the error back up the stack, each application can handle an error from MySqrt() in a way that is the most context appropriate for it! Ultimately, this keeps MySqrt() as modular as possible, and the error handling can be placed in the less-modular parts of the code.

Another stack unwinding example

Here’s another example showing stack unwinding in practice, using a larger stack. Although this program is long, it’s pretty simple: main() calls First(), First() calls Second(), Second() calls Third(), Third() calls Last(), and Last() throws an exception.

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
62
63
64
65
66
#include <iostream>
using namespace std;
 
void Last() // called by Third()
{
     cout << "Start Last" << endl;
     cout << "Last throwing int exception" << endl;
     throw -1;
     cout << "End Last" << endl;
 
}
 
void Third() // called by Second()
{
     cout << "Start Third" << endl;
     Last();
     cout << "End Third" << endl;
}
 
void Second() // called by First()
{
     cout << "Start Second" << endl;
     try
     {
         Third();
     }
     catch ( double )
     {
          cerr << "Second caught double exception" << endl;
     }
     cout << "End Second" << endl;
}
 
void First() // called by main()
{
     cout << "Start First" << endl;
     try
     {
         Second();
     }
     catch ( int )
     {
          cerr << "First caught int exception" << endl;
     }
     catch ( double )
     {
          cerr << "First caught double exception" << endl;
     }
     cout << "End First" << endl;
}
 
int main()
{
     cout << "Start main" << endl;
     try
     {
         First();
     }
     catch ( int )
     {
          cerr << "main caught int exception" << endl;
     }
     cout << "End main" << endl;
 
     return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值