Online Book Reader

Home Category

Professional C__ - Marc Gregoire [168]

By Root 1532 0
MyException

main caught MyException: MyException with nested runtime_error

Nested exception: Throwing a runtime_error exception

The preceding main() function uses a dynamic_cast to check for the nested exception. Since you always have to perform this dynamic_cast if you want to check for a nested exception, the standard provides a small wrapper called std::rethrow_if_nested() that does it for you. This wrapper can be used as follows:

int main()

{

try {

doSomething();

} catch (const MyException& e) {

std::cout << __func__ << " caught MyException: " << e.what()

<< std::endl;

try {

std::rethrow_if_nested(e);

} catch (const std::runtime_error& e) {

// Handle nested exception

std::cout << " Nested exception: " << e.what() << std::endl;

}

}

return 0;

}

Code snippet from NestedException\NestedException.cpp

STACK UNWINDING AND CLEANUP


When a piece of code throws an exception, it searches for a catch handler on the stack. This catch handler could be zero or more function calls up the stack of execution. When one is found, the stack is stripped back to the stack level that defines the catch handler by unwinding all intermediate stack frames. Stack unwinding means that the destructors for all locally-scoped names are called and all code remaining in each function past the current point of execution is skipped.

However, in stack unwinding, pointer variables are not freed, and other cleanup is not performed. This behavior can present problems, as the following code demonstrates:

void funcOne() throw(exception);

void funcTwo() throw(exception);

int main()

{

try {

funcOne();

} catch (const exception& e) {

cerr << "Exception caught!" << endl;

return 1;

}

return 0;

}

void funcOne() throw(exception)

{

string str1;

string* str2 = new string();

funcTwo();

delete str2;

}

void funcTwo() throw(exception)

{

ifstream istr;

istr.open("filename");

throw exception();

istr.close();

}

Code snippet from StackUnwinding\BadCode.cpp

When funcTwo() throws an exception, the closest exception handler is in main(). Control then jumps immediately from this line in funcTwo():

throw exception();

to this line in main():

cerr << "Exception caught!" << endl;

In funcTwo(), control remains at the line that threw the exception, so this subsequent line never gets a chance to run:

istr.close();

However, luckily for you, the ifstream destructor is called because istr is a local variable on the stack. The ifstream destructor closes the file for you, so there is no resource leak here. If you had dynamically allocated istr, it would not be destroyed, and the file would not be closed.

In funcOne(), control is at the call to funcTwo(), so this subsequent line never gets a chance to run:

delete str2;

In this case, there really is a memory leak. Stack unwinding does not automatically call delete on str2 for you. However, str1 is destroyed properly because it is a local variable on the stack. Stack unwinding destroys all local variables correctly.

Careless exception handling can lead to memory and resource leaks.

This is one reason you should never mix older C models of allocation (even if you are calling new so it looks like C++) with modern programming methodologies like exceptions. In C++, this situation should be handled by one of the techniques discussed in the following two sections.

Use Smart Pointers

The first and recommended technique is to use smart pointers. They allow you to write code that automatically prevents memory or resource leaks with exception handling. Chapter 21 discusses smart pointers in detail. Smart pointer objects are allocated on the stack and whenever the smart pointer object is destroyed, it frees the underlying resource. Here is an example of the previous funcOne() function but using the unique_ptr (C++11) smart pointer:

#include

using namespace std;

void funcOne() throw(exception)

{

string str1;

unique_ptr str2(new string("hello"));

funcTwo();

}

Code snippet from StackUnwinding\SmartPointer.cpp

The str2 pointer of type string* will automatically

Return Main Page Previous Page Next Page

®Online Book Reader