Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C++

C++ Copy Constructor in depth

4.17/5 (18 votes)
25 May 2010CPOL4 min read 143.7K  
The copy constructor is a special kind of constructor which creates a new object which is a copy of an existing one, and does it efficiently.The copy constructor receives an object of its own class as an argument, and allows to create a new object which is copy of another without building it...
The copy constructor is a special kind of constructor which creates a new object which is a copy of an existing one, and does it efficiently.
The copy constructor receives an object of its own class as an argument, and allows to create a new object which is copy of another without building it from scratch.


Here below is a simple declaration of a copy constructor


class string
{
	string();
	~string();
	string(const string &s)
	{
		copy(s.m_str);
	}
};



Now you can use it as follow:


// create an object which is copy of another object
string s1("hello");
string s2(s1);		// copy constructor activated

// create an object as a copy of a temporary object
string s3(string("abc"));

string s4 = s1;
// object s4 does not activate the constructor, but its copy constructor to make only a copy of s1, rather than building a new object




you have to use const in the argument at the copy constructor to create an object as a copy of a temporary object: e.g. string(const string &s).


to make things clear, you can create a new object as copy of a different object without using a copy constructor, like this:


string s4;
s4.set(s1);




this is an example of inefficient code. Since s4 first call its constructor to build a new object and then it make a bit-wise copy of s1. The whole process of calling the constructor to build an object which next is being rewritten, is wasteful, takes time and resources. Copy constructor allow you to prevents this inefficiency.


Default copy constructor


If the programmer did not declared the copy constructor for a class, the compiler will add its own default copy constructor for the objects derived from that class.
Default copy constructor does a very simple operation, they will do a bit-wise (member-wise) copy of an object, which means that the object will be copied bit by bit.


string s1("hello");
string s2(s1);
string s2 = s1;    //the same as above



There is a danger in copying an object bit by bit, if the object contains pointers since the pointer address will be copied in the process resulting in two different objects that share the same memory buffer. You can imagine what will happen when two copies of an object calls their destructors one after the other. The first object that call its destructor will have no problems, since it will try to deallocate the pointer and succeed, but the second objects destructor try to deallocate a pointer which does not exist anymore and the whole application crashes!


For a situation where an object contain pointers we have to write our own copy constructor that will prevent situations like those mentioned above.





Copy constructor & passing objects as arguments to methods





There are 3 situations in which the copy constructor is called:

  1. When we make copy of an object.
  2. When we pass an object as an argument by value to a method.
  3. When we return an object from a method by value.





we saw the first scenario above, and we will now look at the other two scenarios.




When objects are passed to a function as arguments by value, a bit wise copy of the object will be passed to the function and placed on the stack, therefor the constructor of the object will not be called. It make sense if you think of it, you want to pass an object in a certain state containing the data you need for the function to process, if you wanted it in the initialization state with its default data, you could just create it inside the function instead of passing it as an argument.


When the function end, the constructor of the object will be called. This is also make sense since the object was passed by value and its data will not be needed outside the function scope.




No pay attention to this, the situation in which only the object destructor is called can make great deal of troubles. Think what will happen when the object holds a pointer to some address in the memory. When this object is passed as argument to a function the pointer in the new temporary created object will hold the same address as the original object, since its a bit wise copy. When the function ends, the destructor will free the address pointed by the pointer. From this point, if the destructor of the original object will be called it will try to free an address which already free, and we all know what it the consequences of that ...




lets look at an example to make things clear.
Here we define string class which holds a char* pointer:


class string
{
	// constructor
	string(char* aStr)
	{
		str = new char[sizeof(aStr)];
		strcpy (str,aStr);
	}

	// destructor
	~string() 
	{
		del str;
	}
	
	char *getChars(){ return str; }
	char* str;
};



now we will write a function that receive a string object as an argument.


void function (string str)
{
	// do somthing	
}



Lets look what will happen when we call this function :
void main ()
{
	string str("hello");
	function(str);
	function(str); // program crush
}



The first time we call function(str) everything works properly, when the function ends, the input argument on the stack is destroyed, and its destructor will be called, and delete the pointer.
The second time we call the function , everything still works properly, but when the function ends, the constructor will try now to free the address pointed by str, and crash.

The copy constructor come to help us solve this kind of problems. Here is a solution:

class string
{
	// constructor
	string(char* aStr)
	{
		str = new char[sizeof(aStr)];
		strcpy (str,aStr);
	}

	string(string &strObj)
	{
		tmpStr = strObj.getChars();
		str = new tmpStr[sizeof(tmpStr)];
		strcpy (str,tmpStr);
	}


	// destructor
	~string() 
	{
		del str;
	}

	char* str;
};



The same way we can handle the third scenario where a method returns an object:


class string
{
    public:

	string(char* aStr)
	{
		str = new char[ strlen(aStr) + 1 ];
		strcpy (str,aStr);
	}

	string(string &strObj)
	{
                str = new char[ strObj.str ];
                strcpy( str, strObj.str );
	}

	string &string::operator=(const string &s)
	{
                string temp( s );
                std::swap( temp.str, str );
                return *this;
	}

	// destructor
	~string() 
	{
		delete[] str;
	}

    private:
	char* str;
};


[Ash - corrected a few problems in the last version of the class, they were sticking out like a sore thumb]






























License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)