I read a few tutorials about copy constructors on various web sites, wikipedia and also browsed through the first 5 pages of the "copy constructor" search results here on stackoverflow but I still don't get it :(
I have two questions:
1. What exactly does a copy constructor do?
2. Is a copy constructor always necessary (within a struct which contains a dynamically allocated array)?
I'll try to explain what bothers me and what I am actually asking with an example:
Let's say that I don't want to use the C++ string but instead, create my own "smart" string. I'll only provide the code necessary for my question:
#include <iostream>
#include <cstdlib>
using namespace std;
struct MyString
{
char* str;
int n , i;
MyString(int x, char c)
{
str=(char*)malloc(x+1);
for(n=0; n<x; ++n) str[n] = c;
str[n] = '\0';
}
void print()
{
cout << str << endl;
}
void append(MyString second)
{
str=(char*)realloc(str, (n + second.n + 1) * sizeof(char));
for(i=0; i < second.n; ++i) str[n+i] = second.str[i];
n += second.n;
str[n] = '\0';
}
~MyString()
{
cout << "destructor!" << endl;
free(str);
}
};
int main()
{
MyString A(5, '$'), B(5, '#');
A.print();
B.print();
A.append(B);
A.print();
B.print();
A.append(B);
A.print();
B.print();
}
To keep it simple, the struct contains only one constructor which for a given whole number "n" and a given character "c" produces a string which contains the character "c" repeated "n" times. Example: MyString A(5, '$'); means that A is the string "$$$$$". The function print, prints the string on the screen and the function append, appends one string to another string. Example: A.append(B); means A = A + B or in this example A becomes "$$$$$" + "#####" = "$$$$$#####".
There are a few things to notice:
1. MyString contains a dynamically allocated array.
2. The function append has "MyString" as a parameter.
3. I didn't include a copy constructor.
The function append is declared as follows:
void append(MyString second)
Normally this would mean that the function append receives a copy of an object of the type MyString but since MyString contains a dynamically allocated object, the function will receive a copy of a pointer (if I am correct?) to the original object and treat it as a local copy which means that after doing the appending, a destructor will be called upon that pointer and destroy that object so looking at my original main function:
int main()
{
MyString A(5, '$'), B(5, '#');
A.print();
B.print();
A.append(B);
// B doesn't exist anymore
A.print(); // OK
B.print(); // ???
A.append(B); // ???
A.print(); // ???
B.print(); // ???
}
To fix the problem, I could write a copy constructor but do I really need one? I could write the function append as follows:
void append(MyString const& second)
or just
void append(MyString& second)
and this works but I was told that every time I encounter an object which involves dynamic allocation + a function which has the object type as a parameter, I should write a copy constructor, just to be safe. But what could go wrong? What could an user do to mess things up if I don't add a copy constructor?
I could write a copy constructor as follows:
MyString(MyString const& second)
{
n = second.n;
str = (char*)malloc(n);
for(i=0; i<n; ++i) str[i] = second.str[i];
}
and then I can leave the function append in it's original form:
void append(MyString second)
but what exactly happens when the following line is executed?
A.append(B);
I was told that constructors don't have a return value. So, if B calls his copy constructor before the function append (within A) is executed, how exactly is B "telling" A where to look for the copy of B?
And now I see that this question is already too big :( so I'll stop it here for now. Any edits, suggestions, comments and answers are welcome! Thanks in advance!