WEBVTT

00:00.120 --> 00:06.180
Hello again! In this video, we are going to look at writing exception-safe classes. We have been building

00:06.180 --> 00:12.960
up this class, which is similar to the library string, using the principles of RAII, in which we

00:12.960 --> 00:17.400
allocate the resource in the constructor and we release it in the destructor.

00:18.590 --> 00:23.870
Now that we know about exception safety, we want to know: is this class exception safe?

00:24.380 --> 00:30.020
So, in this video, we are going to go throw the member functions of this class and review them for exceptional

00:30.020 --> 00:30.440
safety.

00:31.730 --> 00:37.760
Let's start off with the constructor, which allocates the memory used by this class. Constructors

00:37.760 --> 00:38.870
cannot be declared

00:38.900 --> 00:39.530
noexcept,

00:39.890 --> 00:43.730
So that probably means we cannot provide the no-throw guarantee.

00:44.180 --> 00:45.110
But what can we do?

00:46.250 --> 00:50.450
We have a call to new, which could throw an exception.

00:52.510 --> 00:59.170
If that happens, then the constructor will return immediately, and this object, which is in the process

00:59.170 --> 01:01.390
of being initialized, will be destroyed.

01:02.080 --> 01:08.320
So as far as the rest of the program is concerned, this object never existed. So everything is as it was

01:08.320 --> 01:11.560
before. And we have the strong exception guarantee.

01:16.440 --> 01:18.930
And the same thing, really for the copy constructor.

01:19.530 --> 01:22.470
We have a call to new, which could throw an exception.

01:22.830 --> 01:26.580
And in that case, the object which is being initialized is destroyed.

01:26.850 --> 01:29.160
So again, we have the strong exception guarantee.

01:33.640 --> 01:40.540
For the destructor we call delete, which is a noexcept function, so we can make our destructor

01:40.570 --> 01:41.020
noexcept.

01:42.290 --> 01:45.140
And that means we provide the no-throw guarantee.

01:45.410 --> 01:48.110
So that is actually the strongest guarantee of all.

01:52.170 --> 01:58.230
The assignment operator is a bit more complicated. If you remember, this needs to perform a deep copy.

01:58.560 --> 02:04.300
It is called on an existing object and it needs to release that object's current memory allocation and

02:04.320 --> 02:06.180
give it a new memory allocation.

02:06.630 --> 02:09.600
So again, we have a new call which can throw.

02:10.380 --> 02:14.140
And if that does throw an exception, the function will return immediately.

02:14.910 --> 02:20.490
If it does not modify the data member, then we have the strong exception guarantee, but we have to

02:20.490 --> 02:21.450
be a bit careful here.

02:22.860 --> 02:26.790
So this is the assignment operator implementation we've been using so far.

02:26.820 --> 02:34.650
(I think! It has been revised a few times.) So we have the self-assignment check, then we release the

02:34.650 --> 02:36.120
existing memory allocation.

02:36.840 --> 02:38.850
Then we get a new allocation.

02:39.600 --> 02:45.180
Then we update the size member, and then we copy over the data. And there is actually a problem with

02:45.180 --> 02:46.050
this implementation.

02:46.410 --> 02:47.790
So, can you see what it is?

02:50.180 --> 02:54.740
The problem is that we call delete before we call new.

02:55.220 --> 03:02.600
So if this allocation throws an exception, so if this new call throws an exception, then data will

03:02.600 --> 03:03.560
have the previous value.

03:03.920 --> 03:06.440
But that is now pointing at memory which is no longer valid.

03:07.010 --> 03:09.230
So we have actually modified this object.

03:09.470 --> 03:14.840
We have changed it from having a valid memory allocation to having an invalid allocation.

03:17.220 --> 03:22.830
So what we need to do is to change the order slightly, so we need to allocate the memory before we

03:22.830 --> 03:23.430
release it.

03:24.360 --> 03:29.810
So that means we need to store this memory somewhere, temporarily. Because we still need the old pointer

03:29.880 --> 03:30.630
to call delete.

03:31.770 --> 03:33.270
So then we allocate the memory.

03:33.780 --> 03:38.040
We release the previous memory allocation, and then we reassign the pointer.

03:38.460 --> 03:42.450
So this is all exception-safe and then we do have the strong exception guarantee.

03:43.230 --> 03:49.980
The downside is that very briefly, we are using twice as much memory as we normally do. And, well,

03:49.980 --> 03:50.940
programming in the real

03:50.940 --> 03:56.970
world is all about making trade-offs. Sometimes having exception-safe is more important, and sometimes

03:56.970 --> 03:59.670
keeping down memory usage is more important.

04:00.300 --> 04:02.670
So, it depends on the problem you are solving, really.

04:04.210 --> 04:05.920
So what can we conclude from all this?

04:06.550 --> 04:14.050
When you use RAII, you get a class which is exception safe, almost without doing anything. You just

04:14.050 --> 04:16.480
need to be a little bit careful about the assignment operator.

04:16.750 --> 04:20.770
And in fact, in the video, I will show you another way to do this, which is much easier.

04:22.120 --> 04:23.770
But that is all for this video.

04:24.250 --> 04:25.090
I will see you next time.

04:25.390 --> 04:26.920
Until then, keep coding!
