Hello again! In this video, we are going to look at synthesised member functions. So, as we have already
touched on,
if you do not define certain special member functions, the compiler will synthesize them for you.
So this is the constructor, the copy constructor and the assignment operator.
There are also some new special member functions in modern C++ related to move operations.
And we are going to look at those later on in the course.
And finally, the destructor.
There are rules for when the compiler will synthesize these. In traditional C++, older C++ before 2011,
the compiler will always fill in the missing special functions. If we do not define a constructor or a
copy constructor - because the copy constructor is regarded as a special kind of constructor - then the
compiler will synthesize a default constructor.
This takes no arguments, and it calls the default constructor for each data member.
So if the data member is built in, that will be default initialized, which means undefined.
If there is no copy constructor, the compiler will synthesize one, and this will just call the copy
constructor for each data member. If there is no assignment operator,
similarly, the compiler will synthesize one which calls the assignment operator for each data member
of the class.
And finally, if there is no destructor, the compiler will synthesize one. And that does nothing because
the destructors for the members are automatically called.
In modern C++, the rules are much more complex.
There is five special member functions instead of three.
And the rules about whether some special functions are synthesized depends on whether some of the other
special member functions are defined.
So we are going to leave that for now.
We will come back to this in the section where we look at move operations,
move semantics.
So here's an example of how the compiler will synthesize these special member functions.
We have our "pet" test class with an int and a string member, but we do not define any special functions, just
this print member function, which is going to display the values of the members.
So there is no constructor or copy constructor, so the compiler will synthesize a default constructor,
and this will just use the default initialization for the data members.
There is no copy constructor, so the compiler will synthesize one, which we have seen before.
It will just initialize the members from the argument.
If there is no assignment operator, then again, well, we have seen that.
And finally, the destructor will do nothing.
So we have a couple of test objects which we default construct, then we have one which we copy
construct.
Then we do an assignment. And we print out the data after each step.
So again, we have these weird undefined values.
The string's default constructor just creates an empty string, so that is not too bad.
But this is really weird.
Let's try and make this look a bit better. So let's add a constructor, which takes some arguments.
So we are going to initialize the members from these arguments.
Then we do not need to do anything else.
And then we can give these some sensible initial values.
So we initialize them with a string literal. We could just use a character string, but we might as well get used
to using the newer versions. And let's see what we get now.
So that's better.
(So that is not actually.. The, er...) The output is slightly wrong. That is not the default constructor anymore.
That is now calling the constructor, which takes these arguments.
So "i" is 5 and "str" is "Hello".
And then test2 has 7 and "Goodbye".
And test3 is the same as test. And then we overwrite test with test2, so we get 7 and
"Goodbye".
This ability of the compiler to synthesize special member functions is quite useful. If we have a class
and we do not need the constructor, destructor, et cetera, to do anything special, then we can just
leave them. And the compiler will automatically provide ones which do the right thing.
On the other hand, if we do need to implement them and we do not implement them, the compiler will
not give us an error or a warning.
Instead, it will replace them with ones which do the wrong thing.
For example, if we have members which are built-in types, they will be default initialized and we
will get the wrong values.
If we have a member which is a pointer to allocated memory or a resource handle, then the compiler's
copy constructor and assignment operator will perform what is known as a "shallow" copy.
So this means they just overwrite the data in the target object.
In this case, we need to provide the functions ourselves to make sure that the correct operations are
performed.
If we need the built-in types to have correct default values, we need to provide a default constructor which will
initialize them. If we need to perform a so-called "deep copy", which will allocates new memory, obtained
a new resource for the target object and then initialize it with the same data as the source object.
Then we need to implement the copy constructor and the assignment operator.
We are going to look at shallow and deep copying in more depth.
(Get it? Deep copy? Depth? Never mind!)
We are going to look at that in more detail in the next video.
But until then, keep coding!