WEBVTT

00:00:00.440 --> 00:00:05.130
Copy semantics is the term in C++ which describes how to create

00:00:05.130 --> 00:00:08.750
copies of an object that belongs to the certain class.

00:00:09.240 --> 00:00:11.930
Before we implemented the copy constructor,

00:00:11.940 --> 00:00:14.920
our object class would do a shallow copy,

00:00:14.930 --> 00:00:18.070
which means that it would just literally take the value of a

00:00:18.070 --> 00:00:21.660
pointer and ignore the resource that it points to.

00:00:22.040 --> 00:00:25.590
By introducing the copy constructor, we forced the object

00:00:25.590 --> 00:00:30.860
to make a deep copy instead. But copy constructor is just

00:00:30.870 --> 00:00:32.750
one part of the semantics.

00:00:33.240 --> 00:00:37.000
What if instead of initializing this sheep clone at the declaration,

00:00:37.010 --> 00:00:41.620
I just declared it as an empty subject and then decided to later assign

00:00:41.620 --> 00:00:45.740
this original sheep to the clone object? This statement looks really

00:00:45.740 --> 00:00:49.050
similar to the one that invoked the copy constructor.

00:00:49.840 --> 00:00:51.910
However, if I compile this,

00:00:51.920 --> 00:00:55.100
you can see that we are again introduced with the same problems

00:00:55.100 --> 00:00:57.850
we had before defining the copy constructor.

00:00:58.640 --> 00:01:02.750
This is because our new statement does not invoke the copy constructor,

00:01:03.140 --> 00:01:06.020
and that actually makes sense, because as you can see,

00:01:06.030 --> 00:01:09.470
I already declared the sheep clone object on this line,

00:01:09.480 --> 00:01:12.260
which means that the default constructor was called.

00:01:12.640 --> 00:01:14.790
So when we get to this statement,

00:01:14.800 --> 00:01:19.590
this clone object is already instantiated, and copy constructor,

00:01:19.590 --> 00:01:23.890
just like any other constructor, is used only when we need to create

00:01:23.900 --> 00:01:28.580
a new object. Because of that, this statement will actually use the

00:01:28.590 --> 00:01:31.260
assignment operator to make a copy.

00:01:31.270 --> 00:01:33.270
Just like a copy constructor,

00:01:33.280 --> 00:01:37.270
assignment operator is automatically defined by the compiler, and

00:01:37.270 --> 00:01:40.150
its default behavior is to do a shallow copy.

00:01:40.540 --> 00:01:44.180
So, this statement is actually equal to this one.

00:01:44.190 --> 00:01:48.610
The assignment operator works like a function of the clone, which takes in the

00:01:48.620 --> 00:01:52.890
original sheep object as an argument. To fix this problem,

00:01:52.890 --> 00:01:57.130
we again have to redefine the copy behavior to make a deep copy.

00:01:57.170 --> 00:02:00.880
Since the assignment operator basically makes a copy of an object, in this

00:02:00.880 --> 00:02:04.860
context it is also known as a copy assignment operator.

00:02:05.540 --> 00:02:09.430
We need both the copy constructor and the copy assignment

00:02:09.430 --> 00:02:12.460
operator to fully implement copy semantics.

00:02:12.840 --> 00:02:17.230
The default overloaded operator looks just like a copy constructor.

00:02:17.240 --> 00:02:21.450
It takes in a reference and it makes a copy of all the members.

00:02:22.340 --> 00:02:27.050
Let's change it to make it work more like our own redefined copy constructor.

00:02:28.140 --> 00:02:32.270
This code alone will cause a memory leak, because we are reassigning

00:02:32.270 --> 00:02:35.460
this sample pointer to a new object on the heap.

00:02:35.840 --> 00:02:39.920
It's okay to do this inside of the copy constructor, but in this case, the

00:02:39.930 --> 00:02:44.270
object that calls this operator already exists, which means that its

00:02:44.270 --> 00:02:47.560
sample pointer is already pointing to something.

00:02:48.340 --> 00:02:50.230
If we just reassign this pointer,

00:02:50.230 --> 00:02:53.710
the old DNA object that the pointer was pointing to will still stay in

00:02:53.710 --> 00:02:57.250
memory and we won't be able to release it because we don't have its

00:02:57.250 --> 00:03:01.090
memory address. To prevent this, we can first delete the current

00:03:01.090 --> 00:03:05.630
object from the heap before reassigning the pointer, and this would

00:03:05.630 --> 00:03:07.550
work, but we can do better.

00:03:08.140 --> 00:03:10.360
Let's see what's actually happening here.

00:03:10.940 --> 00:03:16.090
We are first deleting the old DNA from memory and then allocating space for

00:03:16.090 --> 00:03:20.790
the new one on the heap, and then we pass the DNA object from the other

00:03:20.790 --> 00:03:23.780
subject to invoke the DNA's copy constructor,

00:03:23.790 --> 00:03:26.960
which will just copy all of the elements from the code array.

00:03:27.340 --> 00:03:31.510
So, why don't we just skip this deleting and allocating new memory, and

00:03:31.510 --> 00:03:36.410
instead just do what this copy constructor does? I will first include the

00:03:36.420 --> 00:03:39.760
algorithm library to get access to the copy function.

00:03:40.540 --> 00:03:43.360
We'll need it for making a copy of an array.

00:03:44.040 --> 00:03:47.640
I will also use the define preprocessor directive to create

00:03:47.640 --> 00:03:50.520
a macro for the size of the DNA code.

00:03:50.530 --> 00:03:54.420
This is because we'll need this number to make a copy of an array,

00:03:54.420 --> 00:03:57.630
and since we need to use it in more than one place,

00:03:57.640 --> 00:04:02.490
I like to define a macro to make the code more maintainable. In case you

00:04:02.490 --> 00:04:06.470
don't know, preprocessor directive works kind of like a search and replace

00:04:06.470 --> 00:04:09.350
command. Before the actual program gets compiled,

00:04:09.360 --> 00:04:13.810
it will search for all of the places where this DNA code size is written,

00:04:13.820 --> 00:04:17.760
and literally replace it with the number we specified.

00:04:18.140 --> 00:04:21.930
This is another way to define the size of static arrays, because pre

00:04:21.930 --> 00:04:25.390
processor directives execute before compilation,

00:04:25.400 --> 00:04:28.360
hence the size is known at compile time.

00:04:28.940 --> 00:04:31.560
I can now use the standard copy function from the

00:04:31.570 --> 00:04:34.660
algorithm library to make a copy of the array.

00:04:35.140 --> 00:04:38.980
The first two arguments represent the beginning and the end of the array,

00:04:38.980 --> 00:04:42.970
which we want to copy. As we know, the name of the array itself

00:04:42.980 --> 00:04:45.460
is the memory address of the first element.

00:04:46.040 --> 00:04:48.890
The last memory address can be accessed by adding the

00:04:48.890 --> 00:04:51.460
number of elements to the starting address.

00:04:51.840 --> 00:04:53.390
Note that by doing this,

00:04:53.390 --> 00:04:57.840
we are not actually getting the address of the last element, but the address of

00:04:57.840 --> 00:05:01.460
the first free byte in memory after the end of the array.

00:05:02.240 --> 00:05:05.350
Finally, the last argument is the start of the array

00:05:05.350 --> 00:05:07.850
which will accept the copied elements.

00:05:08.540 --> 00:05:11.300
This code will improve the performance of the assignment

00:05:11.300 --> 00:05:14.420
operator, because we don't have to allocate new space on the

00:05:14.420 --> 00:05:17.750
heap, we are overwriting the existing DNA code.

00:05:18.640 --> 00:05:19.990
Before we wrap this up,

00:05:20.000 --> 00:05:24.160
there is still one thing that we need to add to this operator definition.

00:05:24.640 --> 00:05:27.880
A common practice of assignment operators is to return the

00:05:27.880 --> 00:05:30.460
reference to the current object itself.

00:05:30.840 --> 00:05:34.550
This is because of the possibility that someone might try to do a

00:05:34.550 --> 00:05:38.160
multiple assignment, which could look something like this.

00:05:39.340 --> 00:05:42.840
We expect both of these subjects to contain the same value taken

00:05:42.840 --> 00:05:45.820
from the object on the right side of the statement.

00:05:46.030 --> 00:05:49.470
But this would not happen with our current implementation,

00:05:49.480 --> 00:05:52.750
because this statement is equal to this one.

00:05:53.940 --> 00:05:58.480
The first assignment operator would work, but since the operator returns void,

00:05:58.490 --> 00:06:03.100
this subject would be assigned to nothing. To fix this,

00:06:03.110 --> 00:06:06.460
I will return the modified object itself, the object

00:06:06.470 --> 00:06:08.860
that makes a call to this operator.

00:06:09.740 --> 00:06:14.030
We didn't talk about "this" keyword yet, because I don't like to use it

00:06:14.040 --> 00:06:18.120
unless I have to, and I think that this operator is the perfect use case

00:06:18.120 --> 00:06:23.500
where using "this" is not redundant. When you use the this" keyword inside

00:06:23.500 --> 00:06:27.370
of the class's non‑static member function definition, you are actually

00:06:27.370 --> 00:06:31.770
getting a pointer to the current instantiated object which makes a call to

00:06:31.770 --> 00:06:32.650
this function.

00:06:33.040 --> 00:06:36.450
So in our example, the sheep clone object is calling

00:06:36.450 --> 00:06:38.300
this assignment operator function.

00:06:38.310 --> 00:06:43.500
So, the keyword "this" inside of the scope of this function will be a pointer

00:06:43.500 --> 00:06:48.790
to the clone sheep itself. To access data members of this object, we can just

00:06:48.790 --> 00:06:52.620
use their identifier, and the compiler will know that we want to access the

00:06:52.620 --> 00:06:54.960
sample pointer of the current object.

00:06:55.440 --> 00:07:00.560
But if we want to be explicit, we can also access the sample pointer like this.

00:07:00.940 --> 00:07:04.170
Since "this" is a pointer to a current object,

00:07:04.180 --> 00:07:08.760
then we can use the arrow operator to get the sample pointer of that object.

00:07:09.640 --> 00:07:13.210
Some people prefer this practice, but I find it redundant and

00:07:13.220 --> 00:07:16.670
only use "this" keyword if it makes sense in the specific

00:07:16.670 --> 00:07:20.750
context, and in this context, it does make sense. Since we want

00:07:20.750 --> 00:07:22.410
to return the object itself,

00:07:22.420 --> 00:07:27.460
I can use the dereference operator to access it from the "this" pointer.

00:07:28.340 --> 00:07:31.350
Finally, we also have to account for the case when someone

00:07:31.350 --> 00:07:34.360
tries to assign the same object to itself.

00:07:35.140 --> 00:07:37.970
We can check this by verifying if the pointer to the current

00:07:37.970 --> 00:07:41.260
object has the same address as the other object.

00:07:41.640 --> 00:07:42.450
If it does,

00:07:42.460 --> 00:07:46.010
I will immediately return the object itself, because executing the

00:07:46.010 --> 00:07:48.860
rest of the function would be a waste of performance.

00:07:49.640 --> 00:07:53.380
The operator does not return void anymore, instead, it will

00:07:53.380 --> 00:07:55.830
return a reference to a current object.

00:07:55.840 --> 00:08:00.510
So, in other words, a reference to a subject. And that's it.

00:08:00.520 --> 00:08:00.900
Now,

00:08:00.900 --> 00:08:04.740
let's recompile the program to see if it works. No more

00:08:04.740 --> 00:08:08.300
segmentation faults, so it seems that we did it, we fully

00:08:08.310 --> 00:08:11.560
implemented copy semantics for the subject class.
