Sunday, January 27, 2008

Copy Constructor

A Copy Constructor is a special type of constructor in the C++ programming language used to creat a new objects a copy of another of an existing object. This constructor takes single argument : a reference of the object to be copied.
Normally the compiler automatically creates a copy constructor for each class (known as an implicit copy constructor) but for special cases the programmer creates a copy constructor, known as a explicit copy constructor. In such cases, the complier doesnt create the implicit one.
A copy constructor is generally needed when an object owns pointers or non-sharable references such as to a file and then you usually need a destructor and assignement operator too.
Copying of objects is achieved by the use of a copy constructor and a copy assignment operator. A copy constructor has as its first parameter a (possibly const or volatile) reference to its own class type. It can have more arguments, but the rest must have default values associated with them.

There are four instances when a copy constructor is called:
1) When an object is returned by value
2) When an object is passed (into a function) by value as an argument
3) When an object is constructed based on other object (pf same class)
4) When compiler generates a temporary object (e.g.-as in explicit casting)

Examples:
These examples illustrate how copy constructors work and why they are required sometimes.

The Implicit Copy Constructor

#include "iostream.h"
class Person
{
public:
int age;
Person(int age): age(age) {}
};
int main()
{
Person timmy(10);
Person sally(15);
Person timmy_clone = timmy;
std::cout << timmy.age << " " << sally.age << " " << timmy_clone.age << std::endl;
timmy.age = 23;
std::cout << timmy.age << " " << sally.age << " " << timmy_clone.age << std::endl;
return 0;
}
OutPut:
10 15 10
23 15 10

As expected, timmy has been copied to the new object, timmy_clone. While timmys age was changed, timmy_clones age remained the same. This is because they are totally different objects.
The compiler has generated a copy constructor for us, and it could be written like this:

Person(Person const& copy):age(copy.age) {}

The Explicit Copy Constructor:

#include "iostream.h"
class Array
{
public:
int size;
int* data;
Array(int size): size(size), data(new int[size]) {}
~Array()
{
delete[] data;
}
};
int main()
{
Array first(20);
first.data[0] = 25;
{
Array copy = first;
std::cout << first.data[0] << " " << copy.data[0] << std::endl;
} // (1)

first.data[0] = 10; // (2)
return 0;
}

OutPut:
25 25
Segmentation fault

Since we didn't specify a copy constructor, the compiler generated one for us. The generated constructor would look something like

Array(Array const& copy): size(copy.size), data(copy.data) {}

The problem with this constructor is that it performs a shallow copy of the data pointer. It only copies the address but not the actual data. When the program reaches line (1), copy`s destructor will get called (because objects on the stack are destroyed automatically when their scope ends). As you can see, Arrays destructor deletes the data array, therefore when it deleted copys data, it also deleted firsts data. Line (2) now accesses invalid data and writes to it! This produces the famous Segmentation Fault.
If we write our own copy constructor that performs a deep copy then this problem goes away.

Array(Array const& copy): size(copy.size), data(new int[copy.size])
{
std::copy(copy.data, copy.data + copy.size, data); // #include for std::copy
}

Here, we are creating a new int array and copying the contents to it. Now, copys destructor only deletes its data and not firsts data as well. Line (2) will not produce a segmentation fault anymore.
Instead of doing a deep copy right away, there are some optimization strategies that can be used. These allow you to safely share the same data between several objects, thus saving space. The Copy on write strategy makes a copy of the data only when it is written to. Reference counting keeps the count of how many objects are referencing the data, and will delete it only when this count reaches zero (e.g boost::shared_ptr).

If you still want to know more for Copy constructor then please go through the following link also
(1)http://www.fredosaurus.com/notes-cpp/oop-condestructors/copyconstructors.html
(2)http://cse.stanford.edu/class/cs193d/handouts/14CopyConstructors.pdf

No comments: