Professional C__ - Marc Gregoire [172]
mMatrix = nullptr;
}
Code snippet from ConstructorError\Matrix.cpp
Remember, if an exception leaves a constructor, the destructor for that object will never be called!
You might be wondering what happens when you add inheritance into the mix. Superclass constructors run before subclass constructors. If a subclass constructor throws an exception, how are the resources that the superclass constructor allocated freed?
C++ guarantees that it will run the destructor for any fully constructed “subobjects.” Therefore, any constructor that completes without an exception will cause the corresponding destructor to be run.
Function-Try-Blocks for Constructors
The exception mechanism as discussed up to now in this chapter is perfect to handle exceptions within functions. However, how should you handle exceptions thrown from inside a ctor-initializer of a constructor? This section explains a feature called function-try-blocks, which are capable of catching those exceptions. Most C++ programmers, even experienced C++ programmers don’t know the existence of this feature, even though it was introduced more than a decade ago.
The following piece of pseudo code shows the basic syntax for a function-try-block for a constructor:
MyClass::MyClass()
try
: { /* ... constructor body ... */ } catch (const exception& e) { /* ... */ } As you can see, the try keyword should be right before the start of the ctor-initializer. The catch statements should be after the closing brace for the constructor, actually putting them outside the constructor body. There are a number of restrictions and guidelines that you should keep in mind when using function-try-blocks with constructors: The catch statements will catch any exception either thrown directly or indirectly by the ctor-initializer or by the constructor body. The catch statements have to rethrow the current exception or throw a new exception. If a catch statement doesn’t do this, the run time will automatically rethrow the current exception. When a catch statement catches an exception in a function-try-block, all objects that have already been constructed by the constructor will be destroyed before execution of the catch statement starts. You should not access member variables for the object inside a function-try-block catch statement. The catch statements in a function-try-block cannot use the return keyword to return a value from the function enclosed by it. This is not relevant for constructors because they do not return anything. Based on this list of limitations, function-try-blocks for constructors are useful only in a very limited number of situations: To convert an exception thrown by the ctor-initializer to another exception. To log a message to a log file. Let’s see how to use function-try-blocks with an example. The following code defines a class called SubObject. It has only one constructor, which throws an exception of type runtime_error. class SubObject { public: SubObject(int i) throw(std::runtime_error); }; SubObject::SubObject(int i) throw(std::runtime_error) { throw std::runtime_error("Exception by SubObject ctor"); } Code snippet from FunctionTryBlock\FunctionTryBlocks.cpp The MyClass class has a member variable of type SubObject: class MyClass { public: MyClass() throw(std::runtime_error); protected: SubObject mSubObject; }; Code snippet from FunctionTryBlock\FunctionTryBlocks.cpp The SubObject class does not have a default constructor. This means that you need to initialize mSubObject in the MyClass ctor-initializer. The constructor of the MyClass class will use a function-try-block to catch exceptions thrown in its ctor-initializer: MyClass::MyClass() throw(std::runtime_error) try : mSubObject(42) { /* ... constructor body ... */ } catch (const std::exception& e) { cout << "function-try-block caught: '" << e.what() << "'" << endl; } Code snippet from FunctionTryBlock\FunctionTryBlocks.cpp Remember that catch statements in a function-try-block for a constructor have to