I l@ve RuBoard |
![]() ![]() |
7.5 The Standard ExceptionsIf the new expression cannot acquire memory from the program's free store, it throws a bad_alloc exception object. For example, vector<string>* init_text_vector( ifstream &infile ) { vector<string> *ptext = 0; try { ptext = new vector<string>; // open file and file vector } catch( bad_alloc ) { cerr << "ouch. heap memory exhausted!\n"; // ... clean up and exit } return ptext; } Ordinarily, the assignment statement ptext = new vector<string>; allocates the necessary memory, applies the default vector<string> constructor on the heap object, and then assigns the address of that object to ptext. If the memory to represent a vector<string> object is not available, the default constructor is not invoked, and ptext is not assigned. A bad_alloc exception object is thrown, and control transfers to the associated catch clause following the try block. The exception declaration catch( bad_alloc ) does not declare an exception object because we are interested only in catching the exception type and not in actually manipulating the object within the catch clause. [2]
ptext = new (nothrow) vector<string>;
If we wanted to manipulate the bad_alloc exception object, what operations does it support? The standard library defines an exception class hierarchy rooted by an abstract base class named exception. The exception class declares a virtual function named what() that returns a const char*. Its purpose is to provide a textual description of the exception thrown. The bad_alloc class is derived from the base exception class. It provides its own instances of what(). Under Visual C++, the bad_alloc instance of what() generates the message bad allocation. We can also derive our iterator_overflow class from the base exception class. To do so, we must include the exception standard header file and provide an instance of what(): #include <exception> class iterator_overflow : public exception { public: iterator_overflow( int index, int max ) : _index( index ), _max( max ) {} int index() { return _index; } int max() { return _max; } // overrides exception::what() const char* what() const; private: int _index; int _max; }; The benefit of inheriting iterator_overflow from the standard library exception class hierarchy is that it can now be caught by all code that catches the abstract base exception, including code written before the introduction of the iterator_overflow class. This means that we don't have to retrofit existing code to know about this or that new exception class type or catch exceptions anonymously using a catch-all. The catch clause catch( const exception &ex ) { cerr << ex.what() << endl; } matches any class derived from exception. It prints bad allocation when a bad_alloc exception type is thrown. When an exception type of iterator_overflow is thrown, it prints Internal error: current index 65 exceeds maximum bound: 64. Here is a possible implementation of the iterator_overflow instance of what(). It uses an ostringstream class object to format its output message: #include <sstream> #include <string> const char* iterator_overflow:: what() const { ostringstream ex_msg; static string msg; // writes the output into the in-memory // ostringstream class object, // converting the integer values into // string representation ... ex_msg << "Internal error: current index " << _index << " exceeds maximum bound: " << _max; // extract the string object msg = ex_msg.str(); // extract the const char* representation return msg.c_str(); } The ostringstream class supports in-memory output operations on string objects. It is particularly useful when we need to format multiple data types into a string representation. For example, it automatically converts arithmetic objects, such as _index and _max, into their corresponding string representations without our having to concern ourselves about the amount of necessary storage or the algorithm to effect the conversion. The str() member function returns the string object associated with the ostringstream class. The standard library chose to have what() return a const char* C-style string representation rather than a string class object. This leaves us in an apparent quandary: How do we convert a string class object into a C-style string representation? The string class provides us with the solution: A conversion function, c_str(), that returns a const char* representation of the string is exactly what we need. To use the ostringstream class, we must include the sstream standard header file: #include <sstream> The iostream library also supports an istringstream input class. It is particularly useful if we need to convert string representations of nonstring data, such as integer values and addresses, into the actual multiple data types. See Section 20.8 of [LIPPMAN98] for a discussion and illustration of the use of the string stream classes. For a more extensive discussion of the C++ exception handling facility, see Chapter 11 and Section 19.2 of [LIPPMAN98], and Chapter 14 of [STROUSTRUP97]. For a good discussion of exception-safe design and the general issues of class design in the presence of exception handling, see [SUTTER99]. Exercise 7.1The following function provides absolutely no checking of either possible bad data or possible failure of an operation. Identify all the things that might possibly go wrong within the function (in this exercise, we don't yet worry about possible exceptions raised). int *alloc_and_init( string file_name ) { ifstream infile( file_name ); int elem_cnt; infile >> elem_cnt; int *pi = allocate_array( elem_cnt ); int elem; int index = 0; while ( infile >> elem ) pi[ index++ ] = elem; sort_array( pi, elem_cnt ); register_data( pi ); return pi; } Exercise 7.2The following functions invoked in alloc_and_init() raise the following exception types if they should fail: allocate_array() noMem sort_array() int register_data() string Insert one or more try blocks and associated catch clauses where appropriate to handle these exceptions. Simply print the occurrence of the error within the catch clause. Exercise 7.3Add a pair of exceptions to the Stack class hierarchy of Exercise 5.2 to handle the cases of attempting to pop a stack that is empty and attempting to push a stack that is full. Show the modified pop() and push() member functions. ![]() |
I l@ve RuBoard |
![]() ![]() |