Copy Constructor Tutorial (by Mike Olson)
Copy Constructors are C++'s way of making a copy of an object. It is a constructor that takes an object of the same type as a parameter and sets the new object to be a copy of that object. This constructor is used when the program needs to make a copy of an object, such as when your pass an object by value to a function or when returning an object.
Before getting into a discussion of copy constructors and how they work, consider the difference between passing by value and passing by reference for a simple data type: integers. When you pass an integer by value, the program makes a copy of that value and stores it in a new location only accessible by the function. Consider the following code:
int main() {
int a = 5;
function(a);
}
void function(int b) {
// See picture for the memory state at this point.
}
Memory state for a passing by value scenario:
When passing by reference, the value is stored in only one place, and what is given to the function is a pointer to that location in memory.
int main() {
int a = 5;
function(a);
}
void function(int &b) {
// See picture for the memory state at this point.
}
Memory state for a passing by reference scenario:
Now, if function happens to change the value of b, those changes will be reflected in a when control returns to main.
This should all be very familiar at this point. Now, the question is, what is the difference between these two function prototypes?
void function1(Class object);
void function2(Class &object);
function1 is passed by value, and function2 is passed by reference. So how does your program treat these functions when they are called? For the most part, it treats them exactly the same way it treats an integer! If you pass by value, it makes a copy of the object:
int main() {
Class a;
function1(a);
}
void function1(Class b) {
// See picture for the memory state at this point.
}
Memory state for object passed by value:
If you pass by reference, it just makes a pointer to the object:
int main() {
Class a;
function2(a);
}
void function2(Class &b) {
// See picture for the memory state at this point.
}
Memory state for object passed by reference:
Finally, your question should be, "What if I have really complicated data in my class, like pointers to dynamically allocated arrays? How does my program know how to copy that kind of stuff?" The answer is simple: It doesn't! You have to tell it how.
That's where the copy constructor comes in. It needs to manually copy all the data from the incoming class into the new class. Take this simple example of a String class:
class String {
public:
String();
String(const char *);
String(const String&);
~String();
char * getString();
void setString(const char *);
int getLength();
private:
int length;
char * str;
};
Now, this class has two internal variables, length and str. length is simply an integer, but str is a pointer to an array of characters. So, here is how I will implement the constructors:
#include "String.h"
// Default constructor
String::String() {
length = 0;
str = 0;
}
// Simple constructor
String::String(const char * input_str) {
length = strlen(input_str);
str = new char[length];
strcpy(str,input_str);
}
// Copy constructor
String::String(const String & input_str) {
length = strlen(input_str.str);
str = new char[length];
strcpy(str,input_str.str);
}
String::~String() {
delete [] str;
}
So, in this case, when you pass by value, the state of the memory will be:
The syntax for doing a copy constructor is fairly easy. You simply access the variables of the input object and store them into the variables of the new object. However, remember that simply using the = operator on the various member variables won't always cut it! If you have an array, you will need to use a loop to copy each element. If a member is a pointer, you will need to dynamically allocate a new pointer for your object and copy the values at the pointer's location, not simply copy the actual pointer. See the following example:
#define DEFAULT_SIZE 10
class Array {
public:
Array(); // Default constructor
Array(int); // Constructor with a size argument
Array(const Array&); // Copy constructor
~Array();
// Other public methods would go here
private:
int * array;
int size;
};
Array::Array() {
size = DEFAULT_SIZE;
array = new int[size];
}
Array::Array(int newsize) {
size = newsize;
array = new int[size];
}
Array::Array(const Array & oldArray) {
size = oldArray.size;
array = new int[size];
for(int i=0; i<size; i++) {
array[i] = oldArray.array[i];
}
Array::~Array() {
if (array != NULL) {
delete [] array;
array = NULL;
}
}
If you do not specify a copy constructor, you could get one of two possible outcomes. First, your program will simply crash with a Segmentation Fault, glibc error, or some other similarly scary error. This is the best case. Even worse, the program could compensate by trying to figure out how to do the copy itself, and just copy the address value from the str pointer in object b, giving you something like this:
In this case, if you changed b in function, its changes would be reflected in a when control returns to main even though you passed by value! This could potentially be a very difficult problem to track down if you had it.
I hope this helps give some better understanding of how copy constructors work and why we need them. Email FundComp2-08@googlegroups.com if you have additional questions.