WEBVTT

00:00:00.740 --> 00:00:06.350
Hi, and welcome back to the course about pointers and arrays in C++. My name is

00:00:06.350 --> 00:00:10.790
Mateo, and in this final module, we will go over standard library abstraction

00:00:10.790 --> 00:00:14.550
mechanisms provided by the modern C++ language.

00:00:14.940 --> 00:00:18.850
Now that we know how row pointers and arrays work, we will be able to choose

00:00:18.850 --> 00:00:22.460
the correct standard library class for a specific purpose.

00:00:22.840 --> 00:00:27.330
Let's start by talking about static arrays. I created a wrapper class

00:00:27.330 --> 00:00:32.420
for fixed‑sized arrays, and I named myArray. Before talking about the

00:00:32.420 --> 00:00:35.870
Array class from the standard library, I wanted to show you the idea

00:00:35.870 --> 00:00:37.760
behind its implementation.

00:00:38.240 --> 00:00:41.580
The real standard Array class is a lot more sophisticated.

00:00:41.590 --> 00:00:43.600
I just defined some of the most important

00:00:43.600 --> 00:00:46.250
functionality in the simplest way possible.

00:00:46.640 --> 00:00:50.020
As you can see, this class is templated. And that is, in my

00:00:50.020 --> 00:00:53.680
opinion, the best reason to always use it instead of our row

00:00:53.680 --> 00:00:57.010
c‑style array. In case you're not really familiar with

00:00:57.010 --> 00:00:59.220
templates, let me show you what I mean.

00:00:59.230 --> 00:01:02.530
This templated class accepts two parameters,

00:01:02.540 --> 00:01:07.800
the data type of array elements and the size of the allocated array. By

00:01:07.800 --> 00:01:11.340
size, I, of course, mean the number of elements, and this should

00:01:11.340 --> 00:01:14.850
actually be size_t instead of int, but as I said,

00:01:14.860 --> 00:01:16.060
let's keep it simple.

00:01:16.440 --> 00:01:21.060
So, what happens when I try to instantiate an object from this class?

00:01:21.540 --> 00:01:25.580
I will create three arrays by providing the data type and size to

00:01:25.580 --> 00:01:28.460
the template instead of using square brackets.

00:01:28.840 --> 00:01:32.260
Now let's imagine that I started the compilation of this program.

00:01:32.740 --> 00:01:37.250
The compiler will see that we have three different versions of this array class,

00:01:37.260 --> 00:01:42.550
one with five integers, one with three integers, and one with four doubles.

00:01:42.940 --> 00:01:46.930
Any combination of different data type and size will be treated as a different

00:01:46.930 --> 00:01:51.420
class from the perspective of the compiler. This is because the compiler will

00:01:51.430 --> 00:01:56.090
actually use the template to create a version of this class in the code for

00:01:56.100 --> 00:01:58.760
each combination of template arguments.

00:01:59.740 --> 00:02:02.860
For example, a new definition of this class will be created by

00:02:02.860 --> 00:02:07.750
replacing this T typename with int and size with 5.

00:02:08.139 --> 00:02:13.150
This will allow us to make the first array object, which contains five integers.

00:02:13.540 --> 00:02:16.940
But if we did this by hand, we would have to write a version of this

00:02:16.940 --> 00:02:20.050
class for every possible template argument combination.

00:02:20.440 --> 00:02:22.970
This way, the compiler will do it for us.

00:02:22.980 --> 00:02:26.060
It will create two more versions of this class in code,

00:02:26.070 --> 00:02:29.650
one with three integers and one with four doubles.

00:02:30.040 --> 00:02:33.350
I wanted to make sure that you're familiar with what templates actually

00:02:33.350 --> 00:02:37.950
do because now you can see that the size of each array is stored in this

00:02:37.950 --> 00:02:42.680
template argument. And since template arguments are just injected into

00:02:42.680 --> 00:02:44.360
another version of this class,

00:02:44.370 --> 00:02:48.100
that means that this size value will only exist in the code.

00:02:48.310 --> 00:02:51.460
In other words, it doesn't need to be stored in memory.

00:02:51.840 --> 00:02:55.870
That means that the array instantiated by this class and a regular

00:02:55.870 --> 00:02:59.170
array need the same amount of space in memory.

00:02:59.180 --> 00:03:01.650
They just need to store the array itself.

00:03:02.640 --> 00:03:04.410
However, by using templates,

00:03:04.410 --> 00:03:09.310
we can also remember the size of the array without storing it in memory, and

00:03:09.310 --> 00:03:13.260
also, we can attach a lot of functionality to the class itself.

00:03:13.640 --> 00:03:14.400
For example,

00:03:14.400 --> 00:03:17.710
you can see that I defined the size function, which just

00:03:17.710 --> 00:03:22.290
returns the template argument itself. And since we know the

00:03:22.290 --> 00:03:25.360
size, now I can do something like this.

00:03:25.840 --> 00:03:29.230
The add function works like a square bracket operator.

00:03:29.240 --> 00:03:32.550
It takes in an index and returns a reference to an element

00:03:32.550 --> 00:03:35.350
from the array at that specific position.

00:03:35.740 --> 00:03:37.590
However, unlike this operator,

00:03:37.590 --> 00:03:42.080
the at function will first check if the index is out of bounds, and

00:03:42.080 --> 00:03:45.460
we can do that since now we know the array size.

00:03:46.040 --> 00:03:48.750
The array class from the standard library has a similar

00:03:48.750 --> 00:03:51.620
function, but instead of doing this, it will throw an

00:03:51.620 --> 00:03:54.350
exception and do something more sophisticated.

00:03:55.340 --> 00:03:57.480
I also defined two constructors.

00:03:57.490 --> 00:04:00.560
The default one will just create an empty array.

00:04:00.940 --> 00:04:04.420
The other one will accept the initializer list as a

00:04:04.420 --> 00:04:07.150
list of elements of the data type T.

00:04:08.940 --> 00:04:13.160
I included the initializer_list library to achieve this functionality.

00:04:14.040 --> 00:04:17.480
Once I have this list, I can look through it and assign its

00:04:17.480 --> 00:04:20.959
values to the elements of our internal array.

00:04:21.339 --> 00:04:26.900
We will talk about this kind of for loop in the next lesson. And

00:04:26.900 --> 00:04:29.890
finally, I created a simple fill function, which will just

00:04:29.900 --> 00:04:32.550
initialize the whole array with a single value.

00:04:33.440 --> 00:04:37.250
Now that you're familiar with the class, let's use it to see if it works.

00:04:38.440 --> 00:04:41.440
I will only leave this array of three integers and

00:04:41.450 --> 00:04:44.050
initialize it to some random values.

00:04:44.540 --> 00:04:48.160
This will work because of the initializer_list constructor.

00:04:49.940 --> 00:04:52.910
I will then use the overloaded square brackets operator to

00:04:52.910 --> 00:04:55.250
change the value of the first element.

00:04:55.640 --> 00:05:00.250
This operator will pass the index to the internal array from the object

00:05:00.260 --> 00:05:04.060
and return a reference to the element at that position.

00:05:04.840 --> 00:05:07.480
Now let's print out all of the elements by using the

00:05:07.480 --> 00:05:09.250
size function in the for loop.

00:05:10.240 --> 00:05:13.110
We don't need to define some external constant

00:05:13.110 --> 00:05:15.460
expression to store the size of the array.

00:05:16.340 --> 00:05:20.700
After this, I will change the value of all elements with the fill function.

00:05:20.710 --> 00:05:22.460
Let's set them all to 3.

00:05:23.540 --> 00:05:26.310
I will print out all of the elements again to see if

00:05:26.310 --> 00:05:27.950
the fill function did the job.

00:05:28.340 --> 00:05:31.500
And now, just to see if it works, I will try to access the

00:05:31.500 --> 00:05:35.690
element at the index five, but I will use the at function,

00:05:35.690 --> 00:05:37.250
which has balance checking.

00:05:38.940 --> 00:05:43.760
Okay, let's compile this, and it seems that everything works.

00:05:43.770 --> 00:05:46.570
We managed to use the size function to print the array,

00:05:46.570 --> 00:05:50.980
square brackets to update this value, and fill function

00:05:50.980 --> 00:05:53.150
to reinitialize the elements.

00:05:53.540 --> 00:05:58.250
And we also got this warning message because index 5 is out of bounds.

00:05:58.640 --> 00:06:01.660
This is my simple implementation of an array class.

00:06:01.670 --> 00:06:05.960
Now let's use the real standard array container from the standard library.

00:06:06.940 --> 00:06:12.320
We first need to include the array header. And now, we can replace the

00:06:12.320 --> 00:06:16.560
myArray class from the main function with the std::array class.

00:06:17.840 --> 00:06:21.540
This class takes in the same amount of template arguments, so this

00:06:21.540 --> 00:06:26.310
declaration is fine. Since myArray class was inspired by this standard

00:06:26.310 --> 00:06:31.280
array, everything should still work in the same way, and it does. The

00:06:31.280 --> 00:06:35.680
output is equivalent to the previous one. This program termination was

00:06:35.680 --> 00:06:39.950
caused by the out_of_range exception because we tried to access the element

00:06:39.960 --> 00:06:42.790
at index 5. And like I mentioned,

00:06:42.800 --> 00:06:47.160
the at function in the real array class is more sophisticated than

00:06:47.160 --> 00:06:52.060
mine. As you can see, the array class has a lot more functionality than

00:06:52.060 --> 00:06:57.410
a simple c‑style array, so you should use it in almost all situations

00:06:57.420 --> 00:07:01.360
unless you have a special reason not to because maybe you're working

00:07:01.360 --> 00:07:05.960
with an old library or legacy code, which returns a simple row array,

00:07:05.970 --> 00:07:06.960
like this one.

00:07:08.940 --> 00:07:13.300
If something like this happens, fortunately you can easily convert

00:07:13.300 --> 00:07:19.320
this array into a standard array. In C++20, to_array function was

00:07:19.330 --> 00:07:23.230
added just for this purpose, and that's it.

00:07:23.240 --> 00:07:24.490
That's all we have to do.

00:07:24.500 --> 00:07:30.050
Now we have a standard array with elements from this row array. I would

00:07:30.050 --> 00:07:32.820
like to pass this new standard array to a function,

00:07:32.830 --> 00:07:36.960
the printerArr function, which will output all of the elements to the terminal.

00:07:37.340 --> 00:07:41.410
Of course, we need to define this function, so let's do that.

00:07:41.410 --> 00:07:45.620
We know that row arrays will be decayed into a pointer when we

00:07:45.620 --> 00:07:47.660
use them as a function argument.

00:07:48.640 --> 00:07:51.700
This is not the case with standard arrays. We can pass

00:07:51.700 --> 00:07:54.250
them by reference like any other object.

00:07:55.240 --> 00:08:00.300
However, there is still a catch. When you pass arrays by value or reference,

00:08:00.300 --> 00:08:03.150
you need to also specify the template arguments.

00:08:03.540 --> 00:08:06.180
So this function will only be able to work with

00:08:06.180 --> 00:08:08.850
arrays, which contain three integers.

00:08:09.340 --> 00:08:11.940
Fortunately, there is a better way to do this.

00:08:11.950 --> 00:08:16.210
We can create a function template. Just like the array class,

00:08:16.210 --> 00:08:20.020
the template arguments will contain the data type of elements

00:08:20.030 --> 00:08:21.760
and the number of elements.

00:08:22.340 --> 00:08:27.130
Since this size will be passed down to the arrays template argument, we need

00:08:27.130 --> 00:08:32.799
to turn it into a standard size_t type. Std::array class is more

00:08:32.799 --> 00:08:37.669
sophisticated than my implementation where I used a simple integer to store

00:08:37.669 --> 00:08:43.600
the size. Don't forget to include the cstddef library. We need it for the

00:08:43.600 --> 00:08:48.790
size_t data type. And now we have a templated function, which can receive an

00:08:48.790 --> 00:08:51.450
array of any size and data type.

00:08:51.840 --> 00:08:53.940
When we pass the array from the main function,

00:08:53.940 --> 00:08:56.920
compiler will know that it needs to create a version of this

00:08:56.920 --> 00:09:00.360
function that receives an array of three integers.

00:09:00.740 --> 00:09:03.310
I can easily loop through all of the elements

00:09:03.310 --> 00:09:05.530
because the size function will work.

00:09:05.540 --> 00:09:09.050
We don't need an additional parameter for the array length anymore.

00:09:10.140 --> 00:09:10.760
Okay,

00:09:10.770 --> 00:09:15.110
now is the time to implement this standard array in our own DNA

00:09:15.110 --> 00:09:18.960
class. First, let's include the array library.

00:09:20.040 --> 00:09:24.150
I want to turn this legacy code array into a standard array.

00:09:24.540 --> 00:09:27.870
We first need to provide the data type, which is char, and

00:09:27.870 --> 00:09:31.830
then the DNA_CODE_SIZE, and that's it.

00:09:31.840 --> 00:09:33.720
No major changes to the code.

00:09:33.730 --> 00:09:36.950
I want to leave the rest of the DNA class as it is.

00:09:38.040 --> 00:09:42.860
However, we are required to modify the copy assignment operator.

00:09:43.640 --> 00:09:47.610
The standard copy function takes in a pointer to the beginning of

00:09:47.610 --> 00:09:52.280
the array, but the code member from each one of these samples is now

00:09:52.280 --> 00:09:56.440
a standard array class, and the name of the array class is not a

00:09:56.440 --> 00:09:58.260
pointer to the first element.

00:09:58.640 --> 00:10:02.690
This is actually great because there is a better and simpler way to do

00:10:02.690 --> 00:10:07.230
this with standard arrays. We can just assign the array from the other

00:10:07.230 --> 00:10:10.560
subject to the code array of the current object.

00:10:10.940 --> 00:10:14.450
Std::array class has a copy assignment operator, which will

00:10:14.450 --> 00:10:17.330
copy each element from one array to another.

00:10:17.340 --> 00:10:20.450
So basically, it's doing what we expect it to do.

00:10:20.840 --> 00:10:24.880
That would not be possible with c‑style arrays because this statement would

00:10:24.880 --> 00:10:28.440
try to reassign the array name to point to something else,

00:10:28.450 --> 00:10:31.250
which as we know, is not allowed.

00:10:31.640 --> 00:10:35.520
This is one of the perks of using std::array class, and we will

00:10:35.520 --> 00:10:38.150
learn about more of them in the next lesson.
