You've created a class and during its construction an exception is thrown. Question: should you catch it? Answer, it depends!
Let's explore.
When deciding whether to catch the exception ask yourself a very simple question. If you do catch it can you still leave the class in a usable state? If the answer is yes, then by all means catch it, deal with it and allow construction to continue. If the answer is no then do not catch it! Why? Simple! if you catch and swallow it, how is the code that was attempting to construct your class to know that the class is unusable (or, at least only partially constructed)?
You could add a method that returns a boolean value to identify the class failed to construct but that will only lead to more complications and will just make life hard for everyone; you and the user of our class. You'll be giving them the abitilty to work with a partionally constructed object and the chances are that isn't a sane thing to do.
For example, what happens if the user tries to call a function on your partial construction, and almost certainly, not-so-sane class? What if failure to construct has left the class with an unassigned member pointer and that calling any or all of the member functions requires the pointer to be dereference it? Bad things will happen. It will end in tears. Kaboom!
Your solution? You'll now have to add even more code in each public function to check for this kind of issue to make sure the program doesn't crash. Every new function you add will need this check. You'll then have to decide how to deal with the function call if you can't honour it due to the class being in a insane state. A much more sensible option is to just throw an exception and let it emit from the class, giving the user of your class the option to catch it and deal with the case that construction failed.
Okay, so you decide to let the exception emit, but how does that help? It's pretty simply really when you think about it. The stack will unwind until it hits a catch handler. The original class you tried to construct is now out of scope. This is because try blocks and catch blocks are seperate scopes. At this point the object of your class cannot be used.
By simply throwing and allowing an exception to emit from the constructor of your class you are making it clear to the end user that construction has failed and the class must not be used. In fact, better; because emiting the exception cases the stack to unwind all access to your class will now be out of scope, so the user can't attempt to use the partionally constructed object, even if they wanted to.
It should be noted that whilst you can catch and swallow an exception within the constructor of a class by creating a try/catch inside the function block the same is not true if you use a
function try/catch block. This is a special type of try/catch where the actual function body itself is a try/catch block. These are especially useful in constructor functions because, unlike try/catch blocks inside the constructor function body, these can catch exceptions that are thrown by class members whilst they are being initialised in the classes
constructor list.
In the case of this type of try/catch, even if you don't explicitly re-throw a caught exception the compiler will do it for you. In other words, the compiler is telling you that you really shouldn't be writing constructors that can fail silently. Constructors should be as light as possible and do the bare minimum to create a sane object. Any additional set-up should be the remit of an "initialise" function.
The difference between the constructor and the initialise function is that the constructor's job is just to create a class instance that isn't completely insane. For example, consider a bank account class. The constructor might initialise the balance to zero (not just let it take on some arbitrary value), whereas, the initialise function would then take arguments to actually set the balance of the account for the specific context of use.
This is a trivial example and probably not the best argument for using an initialise function, but it makes the point; constructors are just to create a sane object whilst initialisation functions are to then set up that sane object for actual use.
So far so good, but now here's another question for you: if a class constructor emits an exception what happens when its destructor gets executed? For example, let's say it throws half way through and there are still a number of pointers that are to have memory allocated to them that the destructor is responsible for releasing. How does the destructor know what to do?
Here's the kicker - it doesn't because there is no way it could! More importantly, because it doesn't know it never even bothers trying. What? Yes, it's true, if a class constructor throws and emits an exception the class destructor is never called. Think about it, how can the compiler destruct a class that was never actually constructed? It just wouldn't make any sense.
Of course, this leads to a bit of a dilemma. What happens to the resources that were allocated before the exception was thrown? What releases them? Answer: nothing does! Or, more specifically, nothing can because the destructor never gets executed and by the time you know the class has failed to construct it has already gone out of scope. Basically, you have yourself a big fat resource leak. Life sucks, eh?
The solution is to use the
RAII design pattern. It's a fancy acronym (that stands for "Resource Acquisition Is Initialisation") that basically means use smart pointers (or, at least use management classes for your resources). Although the destructor on the class that emits the exception is not called, any members that have been constructed up until that point do have their own destructors called. This means that as long as your class members are smart pointer type objects that are able to clean up after themselves upon destruction they will take care of cleaning up the partially constructed class for you.
See, life isn't so bad after all. Time for a nice cup of tea, I think!
Further reading:
Comments (0)