WEBVTT

00:00:00.340 --> 00:00:05.140
New and delete are just operators, operators which we can overload

00:00:05.150 --> 00:00:09.050
to do something more custom. In the previous lesson, we learned that

00:00:09.050 --> 00:00:12.630
these operators have two main jobs. They need to call the

00:00:12.630 --> 00:00:16.790
constructor or the destructor, and then they need to allocate or

00:00:16.790 --> 00:00:18.850
deallocate memory on the heap.

00:00:19.040 --> 00:00:20.970
When we overload these operators,

00:00:20.970 --> 00:00:25.020
we can only overload the memory allocation part of the process. Other

00:00:25.020 --> 00:00:29.460
functionality that happens in the background like object initialization will

00:00:29.460 --> 00:00:34.290
stay unchanged. I will hover over the new operator to check out its prototype

00:00:34.290 --> 00:00:39.400
definition. Operators are defined just like functions, but we always need to

00:00:39.400 --> 00:00:45.120
remember that unlike functions, they require this operator keyword. The new

00:00:45.120 --> 00:00:50.050
operator takes in only one parameter, which defines the required size of the

00:00:50.050 --> 00:00:51.860
memory allocation on the heap.

00:00:53.440 --> 00:00:58.420
So, when we do something like this, new int, C++ will check out the

00:00:58.420 --> 00:01:02.610
data type after the operator and calculate how much memory we need to

00:01:02.610 --> 00:01:05.560
allocate to store a value of that size.

00:01:05.840 --> 00:01:10.070
In this case, int will probably take up 4 bytes, so the number 4 will

00:01:10.070 --> 00:01:13.760
be passed in as a size argument of the new operator.

00:01:13.940 --> 00:01:17.130
The data type of this size argument is size_t,

00:01:17.140 --> 00:01:19.250
taken from the standard namespace.

00:01:20.040 --> 00:01:22.190
To use this data type in our example,

00:01:22.200 --> 00:01:25.560
we will need to include the cstddef library.

00:01:26.240 --> 00:01:30.330
The letter c in front informs us that this is a C++ wrapper

00:01:30.340 --> 00:01:34.580
around the old stddef library used in C language.

00:01:34.590 --> 00:01:39.700
This is because the size_t data type is also inherited from C. Size_t

00:01:39.700 --> 00:01:44.100
is used to hold the size of objects in your program, and its own size

00:01:44.100 --> 00:01:46.740
in memory depends on the implementation.

00:01:46.880 --> 00:01:50.410
So basically, whenever you work with object sizes in bytes,

00:01:50.420 --> 00:01:54.080
you should use the size_t data type. At the very least,

00:01:54.090 --> 00:01:57.760
anyone who reads your code will immediately know your intentions.

00:01:57.940 --> 00:02:02.050
We can also see that the return type of this new operator is a void pointer.

00:02:02.240 --> 00:02:04.880
This makes sense because new is used to allocate

00:02:04.880 --> 00:02:07.250
memory for any existing data type.

00:02:07.940 --> 00:02:11.020
Now that we are familiar with the operator's interface, let's

00:02:11.030 --> 00:02:14.190
overload it by defining our own implementation.

00:02:14.200 --> 00:02:16.820
Since I'm defining this in the global scope,

00:02:16.830 --> 00:02:21.730
this overloaded version will be used instead of the default new operator in

00:02:21.740 --> 00:02:26.090
any part of the program. I want you to look at this overloaded operator as

00:02:26.090 --> 00:02:28.660
nothing more than a piece of a larger puzzle,

00:02:28.940 --> 00:02:31.600
a part of a more complicated process.

00:02:31.670 --> 00:02:34.990
Whenever we call new, something will first happen in

00:02:34.990 --> 00:02:36.880
the background. As I mentioned,

00:02:36.880 --> 00:02:41.450
C++ will determine the size we need and pass it down to the operator.

00:02:41.480 --> 00:02:46.100
This is where our overloaded implementation comes in. We need to allocate

00:02:46.100 --> 00:02:50.760
enough memory to store an object of that size and then return the memory

00:02:50.760 --> 00:02:55.550
address from the heap. In most implementations, new operator will actually

00:02:55.550 --> 00:02:59.850
call the malloc function we learned about in one of the previous lessons, and

00:02:59.850 --> 00:03:01.840
this is exactly what we're going to do.

00:03:01.850 --> 00:03:06.960
So let's include the cstdlib library again. We only need

00:03:06.960 --> 00:03:09.050
one line of code to make this work.

00:03:09.940 --> 00:03:13.680
I am passing the required memory size to the malloc function.

00:03:13.880 --> 00:03:17.000
Malloc will then allocate a memory block of that size on the

00:03:17.000 --> 00:03:20.190
heap and return a void pointer, which points to the beginning

00:03:20.190 --> 00:03:22.420
byte of that allocated space.

00:03:22.580 --> 00:03:26.110
We don't need to cast this pointer to any other type because the void

00:03:26.110 --> 00:03:29.650
pointer is exactly what we need to return from this operator.

00:03:30.540 --> 00:03:34.020
Now, let's see the prototype of the delete operator.

00:03:34.180 --> 00:03:39.190
Delete operator will not return anything, which is expected. However,

00:03:39.200 --> 00:03:41.300
it will receive two parameters.

00:03:41.440 --> 00:03:44.490
The first one is the void pointer, which points to the place

00:03:44.490 --> 00:03:46.760
in memory that needs to be deallocated.

00:03:47.040 --> 00:03:50.960
The second parameter is the size of that allocated block of memory.

00:03:52.540 --> 00:03:58.020
So let's overload this operator, too. To implement this, we only

00:03:58.020 --> 00:04:02.070
need the first argument. Since we chose to allocate memory with

00:04:02.070 --> 00:04:05.580
malloc, it is only appropriate to deallocate it with its

00:04:05.580 --> 00:04:08.710
complimentary free function. As you know,

00:04:08.720 --> 00:04:12.560
free only needs a pointer to the place in memory, which needs to be freed.

00:04:13.040 --> 00:04:17.170
So, why would we even consider overloading these operators?

00:04:17.180 --> 00:04:21.790
It seems like we just used these operators as a wrapper around legacy

00:04:21.790 --> 00:04:26.480
allocation functions, and this is true, but we had to start from somewhere.

00:04:26.540 --> 00:04:29.440
Now that our overloaded implementation is working,

00:04:29.450 --> 00:04:32.890
we can start adding our own custom functionality. If you

00:04:32.890 --> 00:04:34.460
remember from the previous lesson,

00:04:34.470 --> 00:04:38.170
I mentioned that a tool like Valgrind will intercept memory allocation

00:04:38.170 --> 00:04:41.260
functions and use them to track down memory leaks.

00:04:41.540 --> 00:04:44.660
This is exactly what we can do now. We can create our own

00:04:44.660 --> 00:04:47.430
primitive version of a memory leak finder.

00:04:47.470 --> 00:04:50.480
We can start out by printing the amount of allocated

00:04:50.480 --> 00:04:53.260
bytes by using the size parameter.

00:04:54.540 --> 00:04:57.590
I can do the same thing inside of the delete operator by

00:04:57.590 --> 00:05:00.260
printing out the amount of deallocated bytes.

00:05:00.840 --> 00:05:03.750
We are just outputting this information to the terminal, but

00:05:03.750 --> 00:05:06.920
imagine what you can do in more robust projects.

00:05:07.010 --> 00:05:10.520
You could use this information to make analytics and graph out

00:05:10.520 --> 00:05:13.050
the progress of allocations through time.

00:05:13.740 --> 00:05:17.810
Let's compile this program again. As you can see, four bytes

00:05:17.810 --> 00:05:20.750
were allocated to store this integer, and then these same

00:05:20.750 --> 00:05:23.150
bytes were deallocated by delete.

00:05:23.940 --> 00:05:28.540
Now let's make a simple memory leak finder. To keep things simple,

00:05:28.550 --> 00:05:31.540
I will only use one global variable to implement this,

00:05:31.550 --> 00:05:34.010
and I will name it allocated_mem.

00:05:34.040 --> 00:05:37.150
We didn't talk about global variables since they're not

00:05:37.150 --> 00:05:39.460
an important topic in this course.

00:05:39.470 --> 00:05:42.660
All you need to know is that global variables are declared

00:05:42.670 --> 00:05:45.960
once the program starts, and they are removed only when the

00:05:45.960 --> 00:05:47.750
whole program is finished.

00:05:48.040 --> 00:05:51.940
They don't belong to any local scope, which means that you can access this

00:05:51.940 --> 00:05:57.000
variable at any time from any function in any part of the code.

00:05:57.040 --> 00:06:00.870
Global variables are also not stored on stack or the heap.

00:06:00.880 --> 00:06:04.410
They have their own special place in memory, but this is all

00:06:04.410 --> 00:06:07.800
I'm going to say about this. We will use this variable to

00:06:07.800 --> 00:06:10.130
track the amount of allocated bytes.

00:06:10.250 --> 00:06:12.040
Whenever the program calls new,

00:06:12.040 --> 00:06:17.460
we will add the amount of bytes to this global variable. When

00:06:17.460 --> 00:06:20.200
the delete is called, we will do the opposite.

00:06:20.240 --> 00:06:24.530
So in theory, if we deallocate all of the memory that was previously allocated,

00:06:24.540 --> 00:06:27.390
this global variables should be 0 at the end of the

00:06:27.390 --> 00:06:31.000
program. To make sure of this, I will create a condition

00:06:31.000 --> 00:06:32.560
at the end of the main function.

00:06:33.740 --> 00:06:37.430
If there is no memory leak, this variable will hold 0.

00:06:37.440 --> 00:06:42.250
And since 0 in C++ is false, this message will not be shown.

00:06:42.260 --> 00:06:45.760
However, if there is a memory leak, we will be notified.

00:06:46.540 --> 00:06:47.950
Let's compile this program.

00:06:48.640 --> 00:06:51.960
No memory leaks because we did everything correctly.

00:06:53.540 --> 00:06:57.050
Now let's pretend that we forgot to deallocate this integer.

00:06:57.840 --> 00:06:59.850
This will result in a memory leak.

00:07:00.840 --> 00:07:02.610
If I compile the program again,

00:07:02.620 --> 00:07:06.640
the message is shown, which means that our simple memory leak

00:07:06.640 --> 00:07:10.430
finder is working. This lesson was a simple demonstration of

00:07:10.430 --> 00:07:14.700
what you can do by overloading these operators. Here are some

00:07:14.710 --> 00:07:17.150
other use cases and ideas.

00:07:17.210 --> 00:07:18.090
For example,

00:07:18.090 --> 00:07:21.260
some embedded systems will not provide you with a heap memory by

00:07:21.260 --> 00:07:25.630
default. Since a lot of standard library containers use dynamic memory

00:07:25.630 --> 00:07:28.820
allocation, this restriction can be very limiting.

00:07:28.880 --> 00:07:32.300
You could implement your own version of the heap by overloading these

00:07:32.300 --> 00:07:36.330
operators. In the previous lesson, we saw how memory allocations on

00:07:36.330 --> 00:07:38.960
the heap are scattered all over the place.

00:07:39.240 --> 00:07:42.740
We can avoid this type of memory fragmentation by preallocating

00:07:42.740 --> 00:07:46.640
larger blocks of memory and then using these blocks to stack more

00:07:46.640 --> 00:07:48.590
objects of the same type together,

00:07:48.720 --> 00:07:53.050
making our allocations faster and storing these objects next to each other.

00:07:53.100 --> 00:07:55.860
You can also have multiple overloads, each of them

00:07:55.860 --> 00:07:58.050
with a different number of arguments.

00:07:58.240 --> 00:08:00.960
These arguments can modify the allocation behavior.

00:08:00.970 --> 00:08:03.940
For example, you may want to pass in the placeholder,

00:08:03.940 --> 00:08:06.850
which will initialize the allocated space in memory.

00:08:07.040 --> 00:08:11.540
You can also handle exceptions differently, reallocate insufficient memory

00:08:11.550 --> 00:08:16.610
or reuse it. In this lesson, we overloaded these operators globally, but you

00:08:16.610 --> 00:08:20.740
can also limit your own implementation to a specific class by overloading

00:08:20.740 --> 00:08:22.970
them inside of the class definition.

00:08:23.030 --> 00:08:25.710
If you are working on projects written in C++,

00:08:25.720 --> 00:08:29.630
chances are you will need to make your code faster, more efficient,

00:08:29.640 --> 00:08:33.750
and optimized by doing more with less resources.

00:08:33.780 --> 00:08:37.860
You can achieve that by adapting the memory allocation strategy to the project

00:08:37.860 --> 00:08:41.460
at hand, and this lesson can serve you as a starting point.
