WEBVTT

00:00:00.340 --> 00:00:01.160
In this lesson,

00:00:01.160 --> 00:00:04.070
we will talk about a hypothetical example to present the

00:00:04.070 --> 00:00:06.540
dangers of dynamic memory allocation.

00:00:06.670 --> 00:00:10.000
Let's imagine that we are working on a simple video game.

00:00:10.000 --> 00:00:12.320
This game does not have an end.

00:00:12.330 --> 00:00:15.400
The goal is to beat as many enemies as you can,

00:00:15.410 --> 00:00:17.260
which will increase your high score.

00:00:17.440 --> 00:00:19.590
You can only fight one enemy at a time,

00:00:19.590 --> 00:00:24.060
and we are responsible for rendering these enemies on the computer screen.

00:00:24.340 --> 00:00:28.300
The game will be over once the player is beaten by one of the enemies.

00:00:28.300 --> 00:00:34.460
This is the code which could hypothetically run this simple game.

00:00:34.940 --> 00:00:35.630
Of course,

00:00:35.630 --> 00:00:38.480
this code wouldn't work because we are just pretending that I

00:00:38.480 --> 00:00:42.070
included some game engine library which provides me with all of

00:00:42.070 --> 00:00:45.190
these useful classes for creating a video game.

00:00:45.200 --> 00:00:49.190
In essence, a video game is just a never‑ending while loop.

00:00:49.190 --> 00:00:53.640
In each iteration, the game is checking if something changed.

00:00:53.650 --> 00:00:56.690
Maybe a key on the joystick was pressed or some enemy

00:00:56.690 --> 00:00:58.600
from the game is attacking you.

00:00:58.610 --> 00:01:01.450
Either way, once the program verifies the changes,

00:01:01.460 --> 00:01:04.360
it needs to render what's supposed to be on the screen.

00:01:05.239 --> 00:01:09.170
Finally, if you exit the game by pressing some button or losing,

00:01:09.170 --> 00:01:12.310
we will break out of the while loop and the game is over.

00:01:12.330 --> 00:01:14.520
We are not interested in game development,

00:01:14.520 --> 00:01:18.860
so we can imagine that all of this logic is already implemented.

00:01:19.540 --> 00:01:22.750
The part that we are responsible for is loading enemies.

00:01:23.540 --> 00:01:26.600
We can imagine that this variable stores a Boolean value.

00:01:26.610 --> 00:01:29.940
So if the new enemy is supposed to be loaded in the game,

00:01:29.950 --> 00:01:32.330
we will call this loadEnemy function.

00:01:32.330 --> 00:01:37.120
Here we declared a pointer to a current_enemy since only

00:01:37.120 --> 00:01:39.260
one enemy can exist on the screen.

00:01:40.040 --> 00:01:44.060
Once this enemy is defeated, there will be another enemy in sight.

00:01:44.540 --> 00:01:47.410
We will load a new enemy with this loadEnemy function,

00:01:47.410 --> 00:01:50.090
point this pointer to a new current_enemy,

00:01:50.100 --> 00:01:51.050
and so on.

00:01:52.240 --> 00:01:54.320
Let's check out this loadEnemy function.

00:01:54.320 --> 00:01:56.180
As you can see,

00:01:56.180 --> 00:01:59.900
we are here allocating space on the heap for this new Enemy character,

00:01:59.910 --> 00:02:02.730
and we store its address in this en pointer.

00:02:02.730 --> 00:02:05.360
Every enemy has some special power,

00:02:05.370 --> 00:02:09.759
and this power is randomly generated so that the game doesn't get predictable.

00:02:09.940 --> 00:02:11.900
Since we are working with a pointer here,

00:02:11.910 --> 00:02:15.960
I had to first dereference it to get to the power property of the object.

00:02:16.240 --> 00:02:18.030
There is a simpler way to do this,

00:02:18.030 --> 00:02:20.250
but more on that in one of the following modules.

00:02:20.250 --> 00:02:21.760
Finally,

00:02:21.760 --> 00:02:24.060
we are returning a pointer to this enemy from the

00:02:24.060 --> 00:02:26.150
heap back to the main function.

00:02:27.740 --> 00:02:29.220
Once the function is finished,

00:02:29.230 --> 00:02:32.070
this local en pointer will be popped off the stack.

00:02:32.070 --> 00:02:36.630
But that's okay because the address of the allocated enemy on

00:02:36.630 --> 00:02:41.190
the heap is now assigned to the current_enemy pointer and the

00:02:41.190 --> 00:02:43.560
enemy is now rendered on the screen.

00:02:44.740 --> 00:02:47.970
This object takes up a large amount of memory on the heap

00:02:47.970 --> 00:02:51.380
because we need to also load textures and other game‑related

00:02:51.380 --> 00:02:56.160
properties of this enemy character, properties which are too big for stack.

00:02:56.940 --> 00:02:58.830
So once we beat the first enemy,

00:02:58.840 --> 00:03:03.450
this Boolean will again be true because there should be another enemy in sight.

00:03:04.440 --> 00:03:07.420
We need to allocate space for a new one and point the

00:03:07.420 --> 00:03:09.930
current_enemy pointer to that place in memory,

00:03:09.940 --> 00:03:12.710
which means the whole process is repeated.

00:03:12.810 --> 00:03:16.460
Notice that every enemy allocates a different amount of memory

00:03:16.470 --> 00:03:19.020
because enemies have different properties.

00:03:19.150 --> 00:03:22.900
In practice, the Enemy object could contain pointers to other dependencies.

00:03:22.900 --> 00:03:25.650
In this case, something like textures.

00:03:25.740 --> 00:03:26.960
But to keep things simple,

00:03:26.960 --> 00:03:30.110
let's just say that all of the memory each enemy needs is

00:03:30.110 --> 00:03:32.960
contained within the Enemy object itself.

00:03:33.840 --> 00:03:37.560
The player will beat the second enemy and the game continues.

00:03:39.940 --> 00:03:44.170
After some time, the player will start to notice that the game is getting slower.

00:03:44.210 --> 00:03:46.940
You can see that we are allocating memory with new,

00:03:46.950 --> 00:03:49.510
but we are not de allocating it with delete.

00:03:49.510 --> 00:03:53.920
When you work on large projects, this is an easy mistake to make,

00:03:53.970 --> 00:03:57.030
especially if the code is larger and more complicated with

00:03:57.030 --> 00:03:59.860
pointers being passed around all over the place.

00:04:00.040 --> 00:04:01.830
When the game loads the next enemy,

00:04:01.840 --> 00:04:04.750
the previous enemy still stays in memory on the heap.

00:04:04.940 --> 00:04:08.160
And since we redirected our pointer to the new enemy,

00:04:08.170 --> 00:04:11.200
we have no way of knowing where these old enemies are,

00:04:11.200 --> 00:04:14.630
which means we cannot deallocate that memory anymore.

00:04:14.630 --> 00:04:20.130
With each new enemy, the memory is slowly being taken away, but never released.

00:04:20.140 --> 00:04:24.160
We can almost imagine that it's leaking, hence the term memory leak.

00:04:24.160 --> 00:04:27.480
Once the player beats a large amount of enemies,

00:04:27.480 --> 00:04:29.570
the program has a problem with finding the

00:04:29.570 --> 00:04:31.550
sufficient amount of memory on the heap.

00:04:32.340 --> 00:04:36.880
Finally, once the program is unable to find enough memory to load new enemies,

00:04:36.890 --> 00:04:38.700
the game will crash.

00:04:38.700 --> 00:04:42.700
This will probably leave you with the bad allocation exception and

00:04:42.700 --> 00:04:46.360
your gaming company with bad reviews and shame.

00:04:47.440 --> 00:04:51.480
You can see that there are still some free unallocated bytes on the heap,

00:04:51.490 --> 00:04:55.690
but each object needs to have the right amount of consecutive bytes,

00:04:55.700 --> 00:04:59.000
large enough to store the whole object in one piece.

00:04:59.000 --> 00:05:02.250
Since the heap doesn't stack objects next to each

00:05:02.250 --> 00:05:05.050
other, this problem is not unusual.

00:05:05.340 --> 00:05:08.450
Now you can see why releasing memory is important and why

00:05:08.450 --> 00:05:10.950
we cannot afford to have memory leaks.

00:05:11.240 --> 00:05:15.130
Newer languages like Python and Ruby have garbage collectors which pay

00:05:15.130 --> 00:05:19.090
attention to unused memory and remove unnecessary objects.

00:05:19.110 --> 00:05:24.290
Of course, this comes at a cost, which is why C++ programs usually run faster,

00:05:24.290 --> 00:05:28.660
but on the other hand, they are arguably more verbose and more complicated.

00:05:28.940 --> 00:05:32.850
C++ developers wanted to give us the best of both worlds, so they

00:05:32.850 --> 00:05:36.540
created wrappers and abstractions around these dangerous dynamic

00:05:36.540 --> 00:05:39.090
allocations, but more on that later.

00:05:39.280 --> 00:05:42.750
Sometimes you will need to use new and delete directly, and in

00:05:42.750 --> 00:05:46.490
those situations, you need to be sure that everything you allocate

00:05:46.500 --> 00:05:49.060
needs to be deallocated at some point.

00:05:49.740 --> 00:05:53.790
Memory leaks are by no means a new problem, so there are a lot of tools

00:05:53.790 --> 00:05:56.770
which can help you find them before they actually occur.

00:05:56.790 --> 00:06:00.390
One of these tools is a software called Valgrind.

00:06:00.450 --> 00:06:01.990
In simplest terms,

00:06:02.000 --> 00:06:05.450
Valgrind will basically run your program while intercepting

00:06:05.450 --> 00:06:09.250
allocations on the heap. So if the number of allocations does

00:06:09.250 --> 00:06:11.590
not match the number of deallocations,

00:06:11.600 --> 00:06:14.260
that is the first sign that you did something wrong.

00:06:14.270 --> 00:06:16.880
Of course, this tool does so much more than that,

00:06:16.890 --> 00:06:19.320
but that is out of the scope of this course.

00:06:19.420 --> 00:06:23.130
In the next lesson, we will learn how to overload new and delete operators,

00:06:23.130 --> 00:06:27.360
which can be used as a simple way to check if our memory is leaking.
