Copy constructor and this pointer 1 Copy constructor and this pointer In C++, we can initialize an object using two slightly different forms of syntax. Consider the following initializations.

int i = 10; OR int i(10); MyString a = "Hello"; OR MyString a("Hello");

Let us contrast the initialization with the assignment.

int i; i = 10; // value of i is reset to 10

Although initialization looks similar to assignment, it is important to understand that they are two entirely different operations. In the above code, involving a basic type such as an int, it may not make any difference; but when we deal with class types, it does make a big difference.

• An initialization is made every time a new object is created.

• In an assignment, no new object is created; the value of an existing object is reset.

In C++, a type of initialization that closely resembles assignment occurs when one object is initialized using another object of the same class.

For example, consider the following declaration.

MyString c = a; // alternatively we can write MyString c(a);

Look at the statement MyString c = a; carefully. This statement creates a new object c and initializes the value of c to the value of a previously created object a. The constructor that performs this type of initialization is called a copy constructor.

If the class does not define a copy constructor, the compiler will provide one for the class.

The compiler supplied default copy constructor performs memberwise copying between the objects; exactly the way the default assignment operator does for the assignment.

Thus, by the declaration MyString c = a; the default copy constructor sets c’s rep value to a.rep. Copy constructor and this pointer 2

Like the default assignment operator, default copy constructor is adequate only if the class has no pointer data members and does not want to customize any behavior on creation.hi

Since MyString class has no copy constructor, the declaration MyString c = a; creates object c as shown below.

However, this is not what we want, because any changes made on object c would affect object a. Object c must be independent from a, with the requirement that its initial state (or value) is same as that of a as illustrated below.

In order to accomplish this, the MyString class must have its own copy constructor as shown below.

MyString(MyString const &ob) { // creates an object with String as in ob rep = new char[strlen(ob.rep) +1]; strcpy(rep, ob.rep); } // end constructor MyString(MyString &ob)

Notice that the function header of a copy constructor has the general form Copy constructor and this pointer 3

class_name(class_name &)

As with all constructors, the function name must be the class name. The parameter list contains only one parameter which is a reference to the class object. This is a characteristic of all copy constructors.

To ensure that the parameter is not inadvertently altered, we specify the parameter to be const as illustrated below.

class_name(const class_name &)

The use of a reference parameter for the copy constructor is essential. (Do you see why? I leave this to you as a self test exercise.) The destructor function A destructor is a member function of the class. Destructors can be thought of as complements of constructors. The destructor function of a class is called when an object of the class is destroyed.

Like a constructor, the destructor function has the same class name but is preceded with a tilde (∼).

Thus, for our MyString class, the destructor name is ∼MyString(). In the absence an explicit destructor defined for the class, the compiler supplies a do- nothing default destructor.

A destructor function takes no arguments; therefore there can be only one destructor function per class.

The class destructor is automatically called whenever a class object goes out of existence. Therefore, destructors are meant to clear any unwanted effects that might be left by the object. For example, keeping a file or network connection open.

A well-defined class Any well-defined class should have the following members, as applicable:

1. default constructor: class_name(); 2. copy constructor: class_name(class_name const &); 3. destructor: ~class_name(); 4. assignment op: class_name &operator=(class_name const &); 5. move constructor: class_name(class_name &&); // (covered later) 6. move assignment op:

Copy constructor and this pointer 4

We want to remark that the return type of operator= function should not be void type.

The version of the class MyString including these functions is given below.

class MyString { private: shared_ptr rep; public: MyString(); // default ctor MyString(string const &); // ctor with ordinary string argument MyString(MyString const &); // copy ctor MyString(MyString &&); // move ctor – discussed later ~MyString(); // destructor

MyString& operator=(MyString const &rhsObject); // assignment operator void showString(); // outputs the String in the object }; // end class MyString

The member function definitions are given in the next page. Copy constructor and this pointer 5

MyString::MyString() { // creates an object with empty string rep = make_shared(); }

MyString::MyString(string str) { // creates an object with an ordinary string pointed to rep = make_shared(str); }

MyString::MyString(MyString const &ob) { // creates an object with String as in ob rep = make_shared(ob.rep); }

MyString::~MyString() {

}

MyString & MyString::operator=(const MyString &ob) { // resets the value of calling object to the value of ob rep = make_shared(ob.rep); return *this; // “this” pointer explained below }

void MyString::showString() { // outputs the string pointed to by rep cout << *rep; }

The following program uses the MyString class. Copy constructor and this pointer 6

#include #include // for strlen and strcpy using namespace std;

// put class MyString here

void main() { MyString a("Hello"), b = "CS 570", c("Students"); // constructor MyString(char *) creates objects a, b, and c

MyString d; // default constructor MyString() creates d

MyString e(a), f = c; // copy constructor MyString(MyString &) creates objects e and f

cout << "String in object a = "; a.showString(); cout << endl; cout << "String in object b = "; b.showString(); cout << endl; cout << "String in object c = "; c.showString(); cout << endl; cout << "String in object d = "; d.showString(); cout << endl; cout << "String in object e = "; e.showString(); cout << endl; cout << "String in object f = "; f.showString(); cout << endl;

d = c; cout << "Object d reset to have String of c : "; d.showString(); cout << endl;

c = a; cout << "Object c reset to have String of a : "; c.showString(); cout << endl;

Copy constructor and this pointer 7

cout << "Object d’s string remains as before : "; d.showString(); cout << endl; } // end main function

The above program produces the following output.

String in object a = Hello String in object b = CS 570 String in object c = Students String in object d = String in object e = Hello String in object f = Students Object d reset to have string of c : Students Object c reset to have string of a : Hello Object d’s String remains as before : Students

Each object of a class maintains its own set of data members. This permits each object to have its own clearly defined state as determined by the values stored in its data members.

For example, in the above program, we have created six objects of type MyString. A distinct area of memory is set aside for the data members of each of these objects.

The this pointer When defining member functions (in the code segment of the body of member functions) for a class, occasionally we may need to refer to the calling object.

The this pointer is a predefined pointer that holds the address of the calling object. For example, consider the following statement.

a.showString(); // outputs the string "Hello"

The member function showString() is called by the MyString object a. Now look at the function definition carefully. The function definition is reproduced below for convenience. Copy constructor and this pointer 8

void MyString::showString() { // outputs the String pointed to by rep cout << *rep; }

This function outputs the String pointed to by rep. An obvious question at this point is: which object’s rep has to be selected by the compiler?

It is here, that the this pointer comes into action.

Any time an object, say someObj, calls a member function of its class, the someObj is placed in the target of the this pointer, and the this pointer is automatically supplied as a hidden argument to the member function.

Therefore, the following way of defining the member function showString() is equivalent to our original version.

void MyString::showString() { // outputs the String pointed to by rep cout << *(this->rep); } // end function showString

Thus, when object a calls showString(), automatically the this pointer points to object a. Hence this->rep corresponds to a.rep.

Notice that this is not the name of the object that called the member function; but is the name of the pointer that points to the calling object.

Inside the body of a member function, *this is the calling object. Since the pointer this is pointing to the calling object, dereferencing this (by *this) yields the object that invoked the member function.

Assignment operator function revisited Usually, we have no need to access the this pointer. However, in some situations, we have to access it.

For example, consider the operator= member function discussed earlier. If it were to be a void function, we cannot use the function in a chain of assignments such as Copy constructor and this pointer 9

a = b = c; because the above statement gets evaluated as

a = (b = c); which is equivalent to the following.

a.operator=( b.operator=(c) );

In the chain expression a = b = c, first object b calls operator= function and passes object c as the argument to the function. Now, the call b.operator(c) returns void. At this point of chain execution, the expression becomes a.operator=(void). Since there is no operator= function that takes a void argument in the class, this leads to an error at compile time.

We therefore returned a reference in our earlier operator= function so that a chain of assignments can be done. The above function returns the calling object itself.