How to handle Exceptions in C++

By: Lakshmi Viewed: 153206 times  Printer Friendly Format    

In C++, an exception is an object that is passed from the area of code where a problem occurs to the part of the code that is going to handle the problem. The type of the exception determines which area of code will handle the problem, and the contents of the object thrown, if any, may be used to provide feedback to the user.

The basic idea behind exceptions is fairly straightforward:

  • The actual allocation of resources (for example, the allocation of memory or the locking of a file) is usually done at a very low level in the program.
  • The logic of what to do when an operation fails, memory cannot be allocated, or a file cannot be locked is usually high in the program, with the code for interacting with the user.
  • Exceptions provide an express path from the code that allocates resources to the code that can handle the error condition. If there are intervening layers of functions, they are given an opportunity to clean up memory allocations, but are not required to include code whose only purpose is to pass along the error condition.

The basic steps in using exceptions are

1. Identify those areas of the program in which you begin an operation that might raise an exception, and put them in try blocks.

2. Create catch blocks to catch the exceptions if they are thrown, to clean up allocated memory, and to inform the user as appropriate. Listing 20.1 illustrates the use of both try blocks and catch blocks.

Definition: Exceptions are objects used to transmit information about a problem.

Definition: A try block is a block surrounded by braces in which an exception may be thrown.

Definition: A catch block is the block immediately following a try block, in which exceptions are handled.

When an exception is thrown (or raised), control transfers to the catch block immediately following the current try block.

NOTE: Some older compilers do not support exceptions. Exceptions are, however, part of the emerging C++ standard. All major compiler vendors have committed to supporting exceptions in their next releases, if they have not already done so. If you have an older compiler, you won't be able to compile and run the exercises in this chapter. It's still a good idea to read through the entire chapter, however, and return to this material when you upgrade your compiler.

Raising an exception.

0:       #include <iostream.h>
2:       const int DefaultSize = 10;
4:       class Array
5:       {
6:       public:
7:          // constructors
8:          Array(int itsSize = DefaultSize);
9:          Array(const Array &rhs);
10:          ~Array() { delete [] pType;}
12:          // operators
13:          Array& operator=(const Array&);
14:          int& operator[](int offSet);
15:          const int& operator[](int offSet) const;
17:          // accessors
18:          int GetitsSize() const { return itsSize; }
20:          // friend function
21:         friend ostream& operator<< (ostream&, const Array&);
23:          class xBoundary {};  // define the exception class
24:       private:
25:          int *pType;
26:          int  itsSize;
27:       };
30:       Array::Array(int size):
31:       itsSize(size)
32:       {
33:          pType = new int[size];
34:          for (int i = 0; i<size; i++)
35:            pType[i] = 0;
36:       }
39:       Array& Array::operator=(const Array &rhs)
40:       {
41:          if (this == &rhs)
42:             return *this;
43:          delete [] pType;
44:          itsSize = rhs.GetitsSize();
45:          pType = new int[itsSize];
46:          for (int i = 0; i<itsSize; i++)
47:             pType[i] = rhs[i];
48:          return *this;
49:       }
51:       Array::Array(const Array &rhs)
52:       {
53:          itsSize = rhs.GetitsSize();
54:          pType = new int[itsSize];
55:          for (int i = 0; i<itsSize; i++)
56:             pType[i] = rhs[i];
57:       }
60:       int& Array::operator[](int offSet)
61:       {
62:          int size = GetitsSize();
63:          if (offSet >= 0 && offSet < GetitsSize())
64:             return pType[offSet];
65:          throw xBoundary();
66:          return pType[0]; // appease MSC 
67:       }
70:       const int& Array::operator[](int offSet) const
71:       {
72:          int mysize = GetitsSize();
73:          if (offSet >= 0 && offSet < GetitsSize())
74:             return pType[offSet];
75:          throw xBoundary();
76:          return pType[0]; // appease MSC 
77:       }
79:       ostream& operator<< (ostream& output, const Array& theArray)
80:       {
81:          for (int i = 0; i<theArray.GetitsSize(); i++)
82:             output << "[" << i << "] " << theArray[i] << endl;
83:          return output;
84:       }
86:       int main()
87:       {
88:          Array intArray(20);
89:          try
90:          {
91:             for (int j = 0; j< 100; j++)
92:             {
93:                intArray[j] = j;
94:                cout << "intArray[" << j << "] okay..." << endl;
95:             }
96:          }
97:          catch (Array::xBoundary)
98:          {
99:             cout << "Unable to process your input!\n";
100:          }
101:          cout << "Done.\n";
102:         return 0;
103: }

Output: intArray[0] okay...
intArray[1] okay...
intArray[2] okay...
intArray[3] okay...
intArray[4] okay...
intArray[5] okay...
intArray[6] okay...
intArray[7] okay...
intArray[8] okay...
intArray[9] okay...
intArray[10] okay...
intArray[11] okay...
intArray[12] okay...
intArray[13] okay...
intArray[14] okay...
intArray[15] okay...
intArray[16] okay...
intArray[17] okay...
intArray[18] okay...
intArray[19] okay...
Unable to process your input!

Analysis: On line 23, a new class is contained within the declaration of the boundary.

This new class is not in any way distinguished as an exception class. It is just a class like any other. This particular class is incredibly simple: It has no data and no methods. Nonetheless, it is a valid class in every way.

In fact, it is incorrect to say it has no methods, because the compiler automatically assigns it a default constructor, destructor, copy constructor, and the copy operator (operator equals); so it actually has four class functions, but no data.

Note that declaring it from within Array serves only to couple the two classes together. "Advanced Inheritance," Array has no special access to xBoundary, nor does xBoundary have preferential access to the members of Array.

On lines 60-66 and 69-75, the offset operators are modified to examine the offset requested and, if it is out of range, to throw the xBoundary class as an exception. The parentheses are required to distinguish between this call to the xBoundary constructor and the use of an enumerated constant. Note that Microsoft requires that you provide a return statement to match the declaration (in this case, returning an integer reference), even though if an exception is thrown on line 65 the code will never reach line 66. This is a compiler bug, proving only that even Microsoft finds this stuff difficult and confusing!

On line 89, the keyword try begins a try block that ends on line 96. Within that try block, 100 integers are added to the array that was declared on line 88.

On line 97, the catch block to catch xBoundary exceptions is declared.

In the driver program on lines 86-103, a try block is created in which each member of the array is initialized. When j (line 91) is incremented to 20, the member at offset 20 is accessed. This causes the test on line 63 to fail, and operator[] raises an xBoundary exception on line 65.

Program control switches to the catch block on line 97, and the exception is caught or handled by the catch on the same line, which prints an error message. Program flow drops through to the end of the catch block on line 100.

Most Viewed Articles (in C++ )

Latest Articles (in C++)

Comment on this tutorial