Hello again! In this video, we are going to have an overview of the assignment operator. The assignment
operator is a member function.
It is called "operator equals".
The assignment operator is automatically called when we assign to existing objects of the same class.
So if we have y = z, and y and z are both objects of class Test, for example.
It is a member function, so it is called on the object on the left of the equals sign, and the object
on the right is passed as the argument to the call.
So this will actually be called as, y dot operator equals, with argument
z.
After the statement has been executed, the members of y, the data members, will have the same values
as the data members in z. And z will be unmodified, of course.
The assignment operator takes its argument z by const reference, and it returns the modified object
so y equals z will return the new value of y.
The reason for returning a value is that we can do multiple assignments in C++.
We are allowed to "chain" operators. And we have already used that when we are outputting several variables
in a single statement.
We chain the left shift operators.
So we can say x equals y equals z.
So x and z will then have the same values as z.
This is processed from right to left.
So it starts by doing y equals z.
So that will call y dot operator equals, with argument
z.
And this will return the new value of y.
Then the new value of y is used as the argument to the next assignment.
So X equals the new value for it.
So this will actually be called as x dot operator equals, and the argument will be the result of y dot operator
equals with argument z, and then the members of x and y will have the same values as z.
The assignment operator actually returns by reference.
That may sound a bit dangerous, but it is actually safe, because it is a reference to the object that
the member function is called on. And that object will still be valid after the member function returns.
You will find some sources which say it should be a const reference, but that is not correct.
It is legal to modify the return value from an assignment, and there are occasions, very rare, when you want
to do that.
In particular, the standard library containers require that you are able to modify it.
If you try to store an object in a container and the assignment operator returns a const reference instead
of a modifiable reference, the code will not compile.
So the correct signature for the assignment operator is operation equals, a member of the class Test.
It will take a Test object by reference to const, and it will return a modifiable reference to a Test object.
So how do you get a reference to the object?
Well, the operator is a member function, so when it is called, it will be given an extra hidden argument,
which is a pointer called "this". And "this" is a pointer to the object that the member function was called
on.
So if we dereference the "this" pointer, after we perform the assignments, then we will get the modified
object.
If we return star this, then we are returning the object. And the compiler will make sure that it is returned
by reference, because the function says it's to be returned by reference. The compiler will handle that bit
for us.
So the rules for when you write an assignment operator are pretty similar to the rules for writing
a copy constructor.
Normally you do not need to bother.
The compiler will synthesize one which calls the assignment operator of all the data members of the
class.
And usually, that is going to be good enough.
There are situations in which that is not going to give the correct behaviour.
And in that case, you do need to write an assignment operator.
And again, it is when the class manages a resource.
But the reasons are slightly different. With an assignment operator,
we have two objects which are already set up and have their own resources.
If we just copy the resource from one class over the resource in the other class, then the original resource
is lost.
For example, if we are dealing with allocated memory and we have a pointer to the memory, if we overwrite
the pointer in the target object, then that pointer is now lost. The original value of the pointer.
So that means we can no longer find the memory, and in particular, we cannot release the memory. So
that memory is still going to be in use, but not available.
So that is a memory leak.
And similarly, with a file handler or a database connection, the handle or the connection is still
in use, but it is no longer available.
The correct way to handle this is to write an assignment operator, and the assignment operator will
release the resource before doing the assignment.
And then when it does the assignment, it will get its own copy of the resource. Which has the same
data as the one that it is being assigned from.
So in effect, this has the same effect as calling the destructor and then calling the copy constructor.
Calling the destructor would release the resource, and calling the copy constructor
would give the target object a new resource, but it has the same values as the one that it is being
assigned from.
As an example of a synthesized assignment operator.
So this is the kind of code that the compiler will generate if we do not do anything. For our class
Test,
we have the assignment operator signature. And then it will just assign the members.
So "i" will be assigned from the "i" value in the argument.
The string will be assigned from the string member in the argument.
And then we dereference the object that we are called on, and we return it, and the compiler will return
that by reference.
So let's add an assignment operator to our String class with a capital "S". We do not actually need one
at the moment, but it will be good practice to get used to writing assignment operators.
And we will need one when we make the class more realistic.
So this is the signature. We have operator equals, which takes a const reference to an object of
the class, and it returns an object by reference.
Then we assign the members.
So this will actually call the assignment operator of the string class and then we get the object
that we were called on, and we return that. And the rest of the code is almost the same.
We have a String object, but this time we are going to assign it to it, instead of copy creating it.
In fact, one thing we should do in here is to add a default constructor.
So this is a default constructor which takes no arguments, and it is just going to initialize the member
string as an empty string.
So we can actually make that an empty string...
OK, so we have our string with "world", then we have a string with "bang". We assigned "world" to it, and
now the string's data is "world".
And then we have an empty string, and we assign "world" to that.
And the data in the string is now "world".
OK, so that all works.
So that is it for this video.
I will see you next time.
But until then, keep coding!