Professional C__ - Marc Gregoire [173]
int main()
{
try {
MyClass m;
} catch (const std::exception& e) {
cout << "main() caught: '" << e.what() << "'" << endl;
}
return 0;
}
Code snippet from FunctionTryBlock\FunctionTryBlocks.cpp
The output of the preceding example is as follows:
function-try-block caught: 'Exception by SubObject ctor'
main() caught: 'Exception by SubObject ctor'
Function-try-blocks are not limited to constructors. They can be used with ordinary functions as well. However, for normal functions, there is no useful reason to use function-try-blocks because they can just as easily be converted to a simple try/catch block inside the function body. One notable difference when using a function-try-block on a normal function compared to a constructor is that rethrowing the current exception or throwing a new exception in the catch statements is not required and the C++ run time will not automatically rethrow the exception.
Errors in Destructors
You should handle all error conditions that arise in destructors in the destructors themselves. You should not let any exceptions be thrown from destructors, for three reasons:
1. Destructors can run while there is another pending exception, in the process of stack unwinding. If you throw an exception from the destructor in the middle of stack unwinding, the program will terminate. For the brave and curious, C++ does provide the ability to determine, in a destructor, whether you are executing as a result of a normal function exit or delete call, or because of stack unwinding. The function uncaught_exception(), declared in the 2. What action would clients take? Clients don’t call destructors explicitly: they call delete, which calls the destructor. If you throw an exception from the destructor, what is a client supposed to do? It can’t call delete on the object again, and it shouldn’t call the destructor explicitly. There is no reasonable action the client can take, so there is no reason to burden that code with exception handling. 3. The destructor is your one chance to free memory and resources used in the object. If you waste your chance by exiting the function early due to an exception, you will never be able to go back and free the memory or resources. Therefore, be careful to catch in a destructor any exceptions that can be thrown by calls you make from the destructor. Normally, destructors call only delete and delete[], which cannot throw exceptions, so there should be no problem. PUTTING IT ALL TOGETHER class GameBoard { public: // general-purpose GameBoard allows user to specify its dimensions GameBoard(int inWidth = kDefaultWidth, int inHeight = kDefaultHeight); GameBoard(const GameBoard& src); // Copy constructor virtual ~GameBoard(); GameBoard& operator=(const GameBoard& rhs); // Assignment operator void setPieceAt(int x, int y, const GamePiece& inPiece); GamePiece& getPieceAt(int x, int y); const GamePiece& getPieceAt(int x, int y) const; int getHeight() const { return mHeight; } int getWidth() const { return mWidth; } static const int kDefaultWidth = 100; static const int kDefaultHeight = 100; protected: void copyFrom(const GameBoard& src); // Objects dynamically allocate space for the game pieces. GamePiece** mCells; int mWidth, mHeight; }; Code snippet from GameBoard\NoExceptions\GameBoard.h And here is the implementation without any exceptions:
Now that you’ve learned about error handling and exceptions, let’s see it all coming together in a bigger example, a GameBoard class. This GameBoard class will come back in Chapter 19. First, here is the definition of the class without any exceptions.