Hello again! In this video, we are going to look at exceptions and special member functions.
We already know that when an exception is thrown, all the local variables in the current scope have
their destructor called. What happens if a destructor throws an exception? If it is handled in the same
destructor, in a catch block, then there is no problem. That is safe.
If the exception is not handled by the destructor, it can be a problem.
The problem can occur if we have an exception, which starts a stack unwind and calls some destructors.
And then one of these destructor starts off a second stack unwind process.
So we have two unwind processes on the same stack, at the same time, and C++ is not designed for that.
So we get undefined behaviour, or a disaster waiting to happen.
So there is a very simple rule: destructors should never throw exceptions. If for some reason you
do need to throw an exception in a destructor,
it should be handled by a catch block in the same destructor.
But it is very unlikely that this will happen.
The destructors are normally used for releasing resources, and that should not throw exceptions.
Exceptions normally occur when you allocate resources, or perhaps during normal processing.
With constructors, if an exception is thrown during a constructor call, which is not handled by the
constructor, then the object which is being created and initialized will be destroyed.
Its data members will be destroyed as well.
And if it is a divisive class, then all the parent parts of the object are destroyed as well.
So this object is completely obliterated from existence!
As far as the rest of the program is concerned, an object does not exist until the constructor has
successfully completed.
So the advice here is that you should not handle exceptions in constructors, but leave them for the caller.
And, that way, the caller that will know that the object has not been successfully created.
On the other hand, if you can actually solve the problem in the constructor and carry on initializing
the object without doing any harm, then that is perfectly reasonable.
The advantage of allowing the caller to handle the exception is that there are only two possible outcomes.
Either,
there is no exception and there is a successfully created object, which is correctly initialized.
Or, you get an exception and no object.
There is no situation in which you get a half-created or half-initialized object, which is going to
cause problems.
Another possibility is for constructors to throw exceptions, to report errors. And in fact, that is
the only way you can report an error from a constructor.
So that is one argument in favour of exceptions and against error codes.
So let's have a look as an example of that.
Here is our StudentGrade class, with a constructor.
And if the grade is not valid, then we are going to throw an exception.
So that is the out_of_range exception with the message, "invalid
grade".
In our main program, we read in the user's input, and then we pass that as the argument to the constructor,
to the StudentGrade.
And we put that inside a try block.
So if this succeeds, then there is no exception, and we can carry on and work with this StudentGrade
object, and we know it is going to be a valid object, a good object.
On the other hand, if something goes wrong, then the constructor will throw an exception.
The program will jump out of this try block and into the catch handler. So there is no possibility that
we could use an invalid
StudentGrade object.
So if I enter a valid grade, say, 55, then that creates the object. There is no exception.
If I enter an invalid grade, then we get the exception.
OK, so that is it for this video.
I will see you next time, but until then, keep coding!