WEBVTT

00:00.120 --> 00:04.830
Hello again! In this video, we are going to look at exceptions and special member functions.

00:06.450 --> 00:12.000
We already know that when an exception is thrown, all the local variables in the current scope have

00:12.000 --> 00:18.990
their destructor called. What happens if a destructor throws an exception? If it is handled in the same

00:19.170 --> 00:21.960
destructor, in a catch block, then there is no problem. That is safe.

00:22.800 --> 00:26.760
If the exception is not handled by the destructor, it can be a problem.

00:29.940 --> 00:35.250
The problem can occur if we have an exception, which starts a stack unwind and calls some destructors.

00:35.760 --> 00:40.020
And then one of these destructor starts off a second stack unwind process.

00:40.470 --> 00:45.750
So we have two unwind processes on the same stack, at the same time, and C++ is not designed for that.

00:46.380 --> 00:49.860
So we get undefined behaviour, or a disaster waiting to happen.

00:53.300 --> 00:59.360
So there is a very simple rule: destructors should never throw exceptions. If for some reason you

00:59.360 --> 01:01.790
do need to throw an exception in a destructor,

01:02.240 --> 01:05.150
it should be handled by a catch block in the same destructor.

01:05.900 --> 01:07.850
But it is very unlikely that this will happen.

01:08.210 --> 01:12.890
The destructors are normally used for releasing resources, and that should not throw exceptions.

01:13.640 --> 01:18.410
Exceptions normally occur when you allocate resources, or perhaps during normal processing.

01:21.830 --> 01:28.100
With constructors, if an exception is thrown during a constructor call, which is not handled by the

01:28.100 --> 01:33.560
constructor, then the object which is being created and initialized will be destroyed.

01:34.460 --> 01:36.350
Its data members will be destroyed as well.

01:36.860 --> 01:41.960
And if it is a divisive class, then all the parent parts of the object are destroyed as well.

01:42.350 --> 01:45.440
So this object is completely obliterated from existence!

01:47.360 --> 01:52.880
As far as the rest of the program is concerned, an object does not exist until the constructor has

01:52.970 --> 01:54.200
successfully completed.

01:56.680 --> 02:01.990
So the advice here is that you should not handle exceptions in constructors, but leave them for the caller.

02:02.530 --> 02:06.700
And, that way, the caller that will know that the object has not been successfully created.

02:08.390 --> 02:12.700
On the other hand, if you can actually solve the problem in the constructor and carry on initializing

02:12.910 --> 02:15.850
the object without doing any harm, then that is perfectly reasonable.

02:18.650 --> 02:23.600
The advantage of allowing the caller to handle the exception is that there are only two possible outcomes.

02:24.080 --> 02:24.510
Either,

02:24.530 --> 02:29.780
there is no exception and there is a successfully created object, which is correctly initialized.

02:30.380 --> 02:32.540
Or, you get an exception and no object.

02:33.380 --> 02:38.180
There is no situation in which you get a half-created or half-initialized object, which is going to

02:38.180 --> 02:38.900
cause problems.

02:42.170 --> 02:47.960
Another possibility is for constructors to throw exceptions, to report errors. And in fact, that is

02:47.960 --> 02:50.300
the only way you can report an error from a constructor.

02:50.780 --> 02:54.560
So that is one argument in favour of exceptions and against error codes.

02:56.120 --> 02:57.620
So let's have a look as an example of that.

02:57.620 --> 03:01.250
Here is our StudentGrade class, with a constructor.

03:01.790 --> 03:06.500
And if the grade is not valid, then we are going to throw an exception.

03:06.830 --> 03:09.670
So that is the out_of_range exception with the message, "invalid

03:09.680 --> 03:10.070
grade".

03:12.240 --> 03:18.720
In our main program, we read in the user's input, and then we pass that as the argument to the constructor,

03:18.930 --> 03:19.860
to the StudentGrade.

03:20.280 --> 03:21.900
And we put that inside a try block.

03:23.100 --> 03:29.490
So if this succeeds, then there is no exception, and we can carry on and work with this StudentGrade

03:29.490 --> 03:32.910
object, and we know it is going to be a valid object, a good object.

03:34.320 --> 03:37.950
On the other hand, if something goes wrong, then the constructor will throw an exception.

03:38.550 --> 03:43.800
The program will jump out of this try block and into the catch handler. So there is no possibility that

03:43.800 --> 03:45.330
we could use an invalid

03:45.330 --> 03:46.080
StudentGrade object.

03:48.780 --> 03:55.470
So if I enter a valid grade, say, 55, then that creates the object. There is no exception.

03:57.210 --> 04:02.100
If I enter an invalid grade, then we get the exception.

04:04.280 --> 04:05.840
OK, so that is it for this video.

04:06.260 --> 04:09.530
I will see you next time, but until then, keep coding!
