Hello again! In this video, we are going to look at constructors in modern C++. As you know, the
constructor is a special member function. It gets called automatically when an object of the class is
created.
And we can use the constructor to initialize the data members of the class and to perform any set up
that we require before we actually start using the object.
If we do not do anything to initialize the data members of the class, they are default initialized.
So if we have a member which is an object of a class, then the default constructor for that class is
going to be called.
If we have members which are of built-in type, then the initial value is undefined.
The initial value will be whatever happens to be in
that memory, interpreted as an int, or a bool, or whatever the type is.
So who is that class. We have not provided a constructor to initialize the values.
We have a print member, function, which is just going to display the values of the members.
We're using the boolalpha manipulator, so we get "true" or "false" instead of 0 or 1
when we print out the bool members. We create an object of the class and then we call the print
member function of the object.
So what do you think we will get?
Well, that is probably not what you were expecting.
So this memory that is being used by the temperature field obviously had some strange data in it.
Maybe a string or some binary data or something.
And door_open, power_on, that is just 50/50.
So what can you do?
The obvious answer is to write a constructor which will initialize these with sensible values.
We could assume that the caller will provide them, or we could have constructors which take default arguments.
Or we could have default arguments in the constructor. C++ 11 has a much nicer solution. In C++ 11,
You can actually initialize data members in the class definition.
So here we're saying that temperature has a default initial value of 2; door_open is false and power_on
is true.
So you just put the initial value in curly brackets, after the name of the member. And then the rest of
the code is the same.
So let's see if this works.
And there we are.
The temperature is true, the door is not open and the power is on.
And if you have some situation where these are not the values you want, you could always write a
constructor, which will initialize them with the desired values.
But if you do know that you want a particular initial value, or there is some sensible default, then
you should always do this. Because having undefined built-in types is always a big problem.
Some compilers will set them to 0, and as we saw with Visual C++, some do not.
So you can get unpredictable results. When you have a more complicated class, you can have different
constructors and you want to do different things with these constructors. So you may want to set some
of the members and not others.
And then there's also something that you need to do for them.
So, as an example of this,
we have updated our refrigerator.
It is now the 21st century.
The refrigerator can connect to the Internet.
Obviously, this is just an example of constructors, so we are not actually going to connect to the
Internet.
We are just going to pretend to.
So we have a mock Internet class, which is a member of our refrigerator.
We want to arrange things so that the refrigerator always connects to the Internet when it starts up.
So from the constructor, we are going to call the connect() and the login() member functions of the
Internet object.
We have a default username and password, and yes, those really are my username password!
(Not!) So for the default constructor, we just use the default values for these members, like we did before.
And we connect to the Internet using these default credentials.
We may also want to be able to set the temperature of the refrigerator when it comes on.
But still the connect using the default credentials.
So in that case, we have a constructor which sets the temperature and then it connects with the default
credentials.
On the other hand, you may be happy with the default temperature, but you want to choose a particular
username and password.
So in that case, you would have a constructon which will use the defaults again, but it will take the user
name and password and use those.
And then finally, you may want to be able to control everything so you can set the temperature, and
the username, and the password.
So we have four different constructors, which more or less do the same thing.
So that's duplicated code, which is always a sign that you might need to think about your design.
One of the famous axioms of programming is DRY, d-r-y, which stands for "Don't Repeat Yourself!". Do
not repeat yourself.
So if you keep writing repeated codes, then you probably need to think about factoring it.
So in this case, we are connecting to the Internet and logging on in all four cases.
So let's check first that this does work.
The rest of the code is the same.
OK, so we have four constructors where we are connecting to the Internet in the body and logging on,
and we should really refactor that into a single function.
So here is that function.
Perhaps it should be private as it is actually part of the implementation of the class.
So this will just connect to the Internet and log in with a given ID and password. And then, in the default
constructor,
we can call this with the default credentials. In the constructor where we set the temperature and log
on with the defaults, and then we call init() with the defaults.
If we use the default temperature and set the username and password, then we call it with the username
and password.
And finally, if you want to set everything, we set the temperature and call it with the username and
password. And there we are.
So that works exactly the same, but we have factored out all the duplicate code.
But we have added another member function.
So the advantage of this is that, if the details of initializing this Internet object change, or perhaps
you have to call some different function or add another argument, then we only need to change it in this function.
We do not need to change all the constructors.
I have always thought it would be nice if C++ gave you some way for a constructor to call another constructor.
And now you can actually do that in C++11.
It is known as a delegating constructor.
So let's look at that.
So the first part of the code is the same.
We have our Internet class and the refrigerator with a member, which is an Internet object.
And then we have the same constructor which sets everything. So this will set the temperature and the
ID and password for logging onto the internet.
And then in the other constructors, we just delegate to this.
So for example, if we use the default constructor, then we call the constructor with no defaults.
We passed the default temperature, 2, as the argument, and then the ID and the password that we want.
Similarly, if we are using the default login and setting the temperature, then we pass the temperature
and the defaults for the credentials. And for the default constructor, we pass all the defaults.
And the rest of the code is the same, and the result is the same.
So the advantage of this is, if you have lots of combinations of constructors with default and non-default
arguments, this makes it a lot easier.
Instead of writing lots of different constructors, you can just delegate to one constructor that
sets everything.
OK, so that is it for this video.
I'll see you next time, but until then, keep coding!