Processing new Failures

The C++ standard specifies that, when operator new fails, it tHRows a bad_alloc exception (defined in header file ). However, some compilers are not compliant with the C++ standard and therefore use the version of new that returns 0 on failure. For example, the Microsoft Visual Studio .NET throws a bad_alloc exception when new fails, while the Microsoft Visual C++ 6.0 returns 0 on new failure.

Compilers vary in their support for new-failure handling. Many older C++ compilers return 0 by default when new fails. Some compilers support new throwing an exception if header file (or ) is included. Other compilers throw bad_alloc by default, regardless of whether header file is included. Consult the compiler documentation to determine the compiler's support for new-failure handling.


In this section, we present three examples of new failing. The first example returns 0 when new fails. The second example uses the version of new that tHRows a bad_alloc exception when new fails. The third example uses function set_new_handler to handle new failures. [Note: The examples in Figs. 16.516.7 allocate large amounts of dynamic memory, which could cause your computer to become sluggish.]

new Returning 0 on Failure

Figure 16.5 demonstrates new returning 0 on failure to allocate the requested amount of memory. The for statement at lines 1324 should loop 50 times and, on each pass, allocate an array of 50,000,000 double values (i.e., 400,000,000 bytes, because a double is normally 8 bytes). The if statement at line 17 tests the result of each new operation to determine whether new allocated the memory successfully. If new fails and returns 0, line 19 prints an error message, and the loop terminates. [Note: We used Microsoft Visual C++ 6.0 to run this example, because Microsoft Visual Studio .NET throws a bad_alloc exception on new failure instead of returning 0.]

Figure 16.5. new returning 0 on failure.

1 // Fig. 16.5: Fig16_05.cpp 2 // Demonstrating pre-standard new returning 0 when memory 3 // is not allocated. 4 #include 5 using std::cerr; 6 using std::cout; 7 8 int main() 9 { 10 double *ptr[ 50 ]; 11 12 // allocate memory for ptr 13 for ( int i = 0; i < 50; i++ ) 14 { 15 ptr[ i ] = new double[ 50000000 ]; 16 17 if ( ptr[ i ] == 0 ) // did new fail to allocate memory 18 { 19 cerr << "Memory allocation failed for ptr[ " << i << " ] "; 20 break; 21 } // end if 22 else // successful memory allocation 23 cout << "Allocated 50000000 doubles in ptr[ " << i << " ] "; 24 } // end for 25 26 return 0; 27 } // end main  

Allocated 50000000 doubles in ptr[ 0 ] Allocated 50000000 doubles in ptr[ 1 ] Allocated 50000000 doubles in ptr[ 2 ] Memory allocation failed for ptr[ 3 ]  


The output shows that the program performed only three iterations before new failed, and the loop terminated. Your output might differ based on the physical memory, disk space available for virtual memory on your system and the compiler you are using.

new Throwing bad_alloc on Failure

Figure 16.6 demonstrates new throwing bad_alloc on failure to allocate the requested memory. The for statement (lines 2024) inside the try block should loop 50 times and, on each pass, allocate an array of 50,000,000 double values. If new fails and throws a bad_alloc exception, the loop terminates, and the program continues at line 28, where the catch handler catches and processes the exception. Lines 3031 print the message "Exception occurred:" followed by the message returned from the base-class-exception version of function what (i.e., an implementation-defined exception-specific message, such as "Allocation Failure" in Microsoft Visual Studio .NET 2003). The output shows that the program performed only three iterations of the loop before new failed and threw the bad_alloc exception. Your output might differ based on the physical memory, disk space available for virtual memory on your system and the compiler you are using.


Figure 16.6. new throwing bad_alloc on failure.

(This item is displayed on page 827 in the print version)

1 // Fig. 16.6: Fig16_06.cpp 2 // Demonstrating standard new throwing bad_alloc when memory 3 // cannot be allocated. 4 #include 5 using std::cerr; 6 using std::cout; 7 using std::endl; 8 9 #include // standard operator new 10 using std::bad_alloc; 11 12 int main() 13 { 14 double *ptr[ 50 ]; 15 16 // allocate memory for ptr 17 try 18 { 19 // allocate memory for ptr[ i ]; new throws bad_alloc on failure 20 for ( int i = 0; i < 50; i++ ) 21 { 22 ptr[ i ] = new double[ 50000000 ]; // may throw exception 23 cout << "Allocated 50000000 doubles in ptr[ " << i << " ] "; 24 } // end for 25 } // end try 26 27 // handle bad_alloc exception 28 catch ( bad_alloc &memoryAllocationException ) 29 { 30 cerr << "Exception occurred: " 31 << memoryAllocationException.what() << endl; 32 } // end catch 33 34 return 0; 35 } // end main  

Allocated 50000000 doubles in ptr[ 0 ] Allocated 50000000 doubles in ptr[ 1 ] Allocated 50000000 doubles in ptr[ 2 ] Exception occurred: bad allocation  

The C++ standard specifies that standard-compliant compilers can continue to use a version of new that returns 0 upon failure. For this purpose, header file defines object nothrow (of type nothrow_t), which is used as follows:

double *ptr = new( nothrow ) double[ 50000000 ];

The preceding statement uses the version of new that does not throw bad_alloc exceptions (i.e., nothrow) to allocate an array of 50,000,000 doubles.

Software Engineering Observation 16.8

To make programs more robust, use the version of new that throws bad_alloc exceptions on failure.

 

Handling new Failures Using Function set_new_handler

An additional feature for handling new failures is function set_new_handler (prototyped in standard header file ). This function takes as its argument a pointer to a function that takes no arguments and returns void. This pointer points to the function that will be called if new fails. This provides the programmer with a uniform approach to handling all new failures, regardless of where a failure occurs in the program. Once set_new_handler registers a new handler in the program, operator new does not throw bad_alloc on failure; rather, it defers the error handling to the new-handler function.

If new allocates memory successfully, it returns a pointer to that memory. If new fails to allocate memory and set_new_handler did not register a new-handler function, new throws a bad_alloc exception. If new fails to allocate memory and a new-handler function has been registered, the new-handler function is called. The C++ standard specifies that the new-handler function should perform one of the following tasks:

  1. Make more memory available by deleting other dynamically allocated memory (or telling the user to close other applications) and return to operator new to attempt to allocate memory again.
  2. Throw an exception of type bad_alloc.
  3. Call function abort or exit (both found in header file ) to terminate the program.

Figure 16.7 demonstrates set_new_handler. Function customNewHandler (lines 1418) prints an error message (line 16), then terminates the program via a call to abort (line 17). The output shows that the program performed only three iterations of the loop before new failed and invoked function customNewHandler. Your output might differ based on the physical memory, disk space available for virtual memory on your system and the compiler you use to compile the program.


Figure 16.7. set_new_handler specifying the function to call when new fails.

1 // Fig. 16.7: Fig16_07.cpp 2 // Demonstrating set_new_handler. 3 #include 4 using std::cerr; 5 using std::cout; 6 7 #include // standard operator new and set_new_handler 8 using std::set_new_handler; 9 10 #include // abort function prototype 11 using std::abort; 12 13 // handle memory allocation failure 14 void customNewHandler() 15 { 16 cerr << "customNewHandler was called"; 17 abort(); 18 } // end function customNewHandler 19 20 // using set_new_handler to handle failed memory allocation 21 int main() 22 { 23 double *ptr[ 50 ]; 24 25 // specify that customNewHandler should be called on 26 // memory allocation failure 27 set_new_handler( customNewHandler ); 28 29 // allocate memory for ptr[ i ]; customNewHandler will be 30 // called on failed memory allocation 31 for ( int i = 0; i < 50; i++ ) 32 { 33 ptr[ i ] = new double[ 50000000 ]; // may throw exception 34 cout << "Allocated 50000000 doubles in ptr[ " << i << " ] "; 35 } // end for 36 37 return 0; 38 } // end main  

Allocated 50000000 doubles in ptr[ 0 ] Allocated 50000000 doubles in ptr[ 1 ] Allocated 50000000 doubles in ptr[ 2 ] customNewHandler was called  

Категории