 Hello and welcome back. In this lecture, we are going to continue our study of constructors in C++. Specifically, we will look at default constructors and copy constructors. Here is a quick recap of some of the relevant topics we have already studied. We have been studying about object-oriented programming with structures and classes. We have seen how to access members and how to control access to members, both data members and member functions. We have seen the role of constructor and destructor functions and we have also taken a closer look at constructors in the last lecture, where we studied about explicit invocation of constructors, how to set default values of parameters to constructors and how to use initialization lists with constructors. In this lecture, we are going to continue our study of constructors. Specifically, we will study about default constructors and copy constructors. Much of this lecture is motivated by the treatment in the book and introduction to programming through C++ by Abhiram G. Ranade published by McGraw-Hell Education in 2014. All examples taken from this book are indicated in slides by the citation AGR book. Recall that when we were studying about the constructors of a particular class, we said that a constructor is going to get invoked automatically whenever an object of the class is allocated. In fact, the sequence is that the object is first allocated and then the constructor is invoked on that object. And we had studied that constructors are actually a very convenient way to initialize data members when an object of a class is allocated. Similarly, when we studied about destructors, we had seen that a destructor gets invoked automatically when an object of the class is deallocated and once again the sequence is the destructor is invoked on the object first and then the object is deallocated. And we have also seen that destructors can be a convenient way to do bookkeeping and cleaning up before deallocating an object. Now, when talking about constructors, we might as well go back and recall the class V3 for three-dimensional vectors that we have already studied in previous lectures. In this class, we have three private data members named x, y, z, all of type double and we had seen two different constructors for this class. One of these constructors did not take any arguments and such a constructor is going to be called a default constructor of the class V3 whereas the other constructor which took arguments is going to be called the non-default constructor of the class V3. Now, suppose for a moment we want to define an array of V3 objects, let us say I want to define an array of size 100 of V3 objects. So, clearly 100 objects of class V3 must be allocated and we have just seen that whenever an object of class V3 is allocated, the constructor function must be called but there were two constructor functions that we just saw. So, which of these constructor functions should be invoked on each of these 100 objects? Well, the answer is that when we are defining an array, then on each of the objects in the array, the default constructor is going to be called. This is the constructor without any arguments. But now, what would happen if we had either forgotten or intentionally not defined a default constructor for V3? Well, if you have absolutely not defined any kind of constructor for a class, then the C++ compiler will provide you a bare bones default constructor. Since it is a default constructor, it will have no parameters and it is absolutely bare bones, it does nothing in its fault. The presence of this default constructor provided by the C++ compiler will however now allow you to define an array of objects. Each object in the array can now be created and the default constructor provided by the compiler can be invoked on it. In a similar way, if you have not provided any destructor for a particular class in your program, the C++ compiler is going to provide you a default destructor which is also a bare bones default destructor. It has no parameters and does nothing in its fault. Now, the tricky situation is here when you have defined a non-default constructor of the class, but you have not defined a default constructor of the same class. So, in this case, C++ compiler is not going to assume that you forgot to define any constructor because you have indeed defined a non-default constructor. And therefore, the C++ compiler will not provide you a bare bones default constructor. In this case, and without a default constructor, you cannot allocate objects in an array of this class. So, therefore, arrays of objects of such a class which does not have a default constructor but is a non-default constructor cannot be defined. So, to avoid such problems, the best practice is whenever you are defining a class, please define a default constructor for that class as well. Now, let us look at another kind of constructors. These are called copy constructors. So, suppose you are creating a new object by making a copy of another object of the same class. So, for example, in this main program here, I have an object named A of class V3 which has been created by invoking the constructor of this class with specific parameters passed to the constructor. And now over here, I am creating another object A1 of class V3 and immediately initializing it to the object A that has already been created. So, here I have an initialization and a declaration. Therefore, I am actually creating a new object by copying an existing object. And this is a situation where you need a copy constructor. Another situation where you need a copy constructor is when you are passing a parameter to a function by value. Here, myfunk takes its parameter by value and so this object A of class V3 has to be copied from the activation record of main to the activation record of myfunk when myfunk is called. And this copying is going to be achieved through the copy constructor of the class V3. A third situation where you need a copy constructor is when a function is returning an object of a particular class. So, here myfunk is returning an object of class V3 and so therefore, this object that is being returned has to be copied from the activation record of myfunk to the activation record of main and this is going to be achieved through the copy constructor. However, it is important to note here that some compilers are often able to optimize away this copying of the object from the activation record of myfunk to the activation record of main and this is allowed in the C++ standard. The C++ standard allows the compiler to optimize away return values of functions and if your compiler is doing this, then you may not see the copy constructor being called in such a case when myfunk is supposed to return a value which is an object of the class V3. So, depending on your compiler, you may or may not see the copy constructor being invoked here. Now, as opposed to these two cases that we saw here where a copy constructor is certainly needed and the third case where a copy constructor may or may not be needed depending on your compiler, consider this assignment statement. In this assignment statement, I have already created the object V. So, here I am not creating a new object V by copying another object to the same class. So, therefore, a normal assignment statement like this does not require a copy constructor. This does not cause a copy constructor to be invoked. This is a simple assignment statement where all the data members of the object in the right hand side of the assignment are copied to the corresponding data members of the left hand side. Therefore, copy constructors are invoked only when a new object is being created by copying another object of the same class. Now, here is our class V3 that we have seen earlier. These are the two ordinary constructors of this class that we have already seen. Here is how the copy constructor for this class might look like. In this particular case, it is particularly uninteresting, but let us take a note of the way in which parameters are passed to the copy constructor. So, this is the copy constructor of the class V3. This is going to be invoked on a newly created object which is going to get all the values of another object of the same class which I am calling source here. And the way the parameter is passed here is that we pass a reference to a constant object of the class V3. So, this is the source object which I am trying to copy to create a new object which is the receiver object of this copy constructor function. And what does this copy constructor function do? Here in this case, it simply takes the x, y, and z data members of source and copies them to the x, y, and z data members of the receiver object which is the object on which this copy constructor function has been invoked. So, the point to note is that whenever you have a copy constructor, the parameter is always passed like this. It is passed as a reference to a constant object of the class V3. Now, just like for ordinary constructors, if you have not specified a copy constructor in your program but your program requires a copy constructor, perhaps because of the reasons we just saw, then the C++ compiler will create a default copy constructor for you. However, this is going to be a rather uninteresting copy constructor. It will simply copy the values of all data members from the source object to the corresponding members of the receiver object. So, this is pretty much like an usual assignment. But in some situations, as we will just now see, such default copy constructors are not good enough and then more interesting user defined copy constructors are needed. So, here is an example of an interesting usage of copy constructor and this example is motivated by a discussion about a similar example in AGR book. In this example, we are trying to define a class MyString to store character strings. The actual characters in the string are going to be stored in this character array called C array but notice that I have not predefined the size of this array instead I have kept the C array data member as simply a pointer to a character so that I can dynamically allocate an array of appropriate size when I am creating an object of class MyString. Length which is an integer type data member basically stores the length of the string. This is the ordinary constructor for the class. It takes as parameter an array of constant characters basically a constant string and in these dot dot dot which I have not expanded here to keep the description simple what you might want to do is you might want to traverse the character array in its string to figure out how many characters are there before the first backslash zero and then you might want to set the length data member here to the appropriate length of the string and you might want to copy all the characters in init string up to and including the first backslash zero into a dynamically allocated array of appropriate size which is going to be stored in this data member C array. So here a dynamic allocation of a character array is going to happen and then in that you are going to store the characters from init string up to and including the first backslash zero. Here is an interesting destructor function. Note what it is doing before the object of class my string is deallocated it is actually deleting the dynamically allocated character array that we just talked about. Here is the copy constructor. The copy constructor is going to be invoked on a newly created object which is supposed to be obtained by copying an existing object and that existing object is source here. So note once again how the parameter is passed here source is the reference of a constant object of class my string. What do we do in this copy constructor the first thing we do is through this initialization list we set the data member length to the value of source dot length and then the next thing that we do is we dynamically allocate a character array of size length plus one and store it in the data member C array of the newly created object which is the receiver object in this case. Why do we have plus one here this is to store the last backslash zero at the end of the string. Of course if this dynamic memory allocation failed then you have to handle the error appropriately otherwise you basically copy all the elements in the character array of the source object starting from index zero all the way up to including up to an including index length into the corresponding character array of the receiver object here. So this will copy all the characters in the source string including the backslash zero at the end of the source string. So you can see that this copy constructor is actually doing something useful and it is different from the ordinary constructor. So in summary what we saw in this lecture is how to use default constructors and their importance in defining arrays we also studied about copy constructors and their importance in creating new objects by copying existing objects. Thank you for your attention.