Copy ctor basically creates an object using a previously created object. But what if we just create an object and then use '=' for element by element assignment. This even works if object is created dynamically. So what can the copy constructor do that the assignment operator cannot?
-
5Construct an object. – Lightness Races in Orbit Nov 22 '16 at 18:10
-
Is the copy ctor more efficient? If so then why? – Tanmay Bhatnagar Nov 22 '16 at 18:12
-
4That's like asking whether a computer is more efficient than a lawnmower. More efficient at what? When? Makes no sense. – Lightness Races in Orbit Nov 22 '16 at 18:13
-
More efficient at copying the values of elements in comparision to the assignment operator. – Tanmay Bhatnagar Nov 22 '16 at 18:23
-
I am not sure that the duplicate actually addresses the question. It explains the difference, but doesn't really provide an example why it is needed - why one can't simply default-construct an empty object and than copy already existing object to the newcomer. I will vote to reopen the question, if it get's reopen, will try to answer from this perspective. – SergeyA Nov 22 '16 at 18:46
1 Answers
There are a number of good reasons to use copy constructor rather than default constructing an empty object and than assigning the new object to some already existing object.
Reason one. Const-correctness
First, let's play a devil advocate and consider the case when it doesn't matter:
struct X {
int i;
};
X x;
x.i = 10;
X y;
y = x; // Option 1
X y(x) // Option 2
In the example above, there is really no difference between option 1 and option 2, other than better readabilty and code reasoning. But, what if we would like to make our y
object a const
object - because we don't want it to change over it's lifetime? Than option 1 would not be valid:
const X y;
y = x; // Compilation error!
So, even on the simplest example, we can tell that even for very simple types, differentiating between assignment and copy-construction is very important for const-correctness. (There could be other examples for the same point).
Reason 2. Code efficiency
Next comes performance implications. Let's consider a very broken and incorrect implementation of string class (illustration only, has no place in life other than that):
struct VeryBrokenString {
char* buffer;
VeryBrokenString() buffer(new char[1]) { strcpy(buffer, ""); }
~VeryBrokenString() { delete[] buffer; }
VeryBrokerString& operator= (const VeryBrokenString& rhs) {
delete[] buffer;
buffer = new [strlen(rhs.buffer) + 1];
strcpy(buffer, rhs.buffer);
return *this;
}
VeryBrokenString(const VeryBrokenString& rhs) {
buffer = new [strlen(rhs.buffer) + 1];
strcpy(buffer, rhs.buffer);
}
};
VeryBrokenString one; // init it somehow :)
VeryBrokenString two;
two = one; // Option 1
VeryBrokenString two(one); //Option 2
In the example above, option 1 leads to duplication of efforts - the buffer is first allocated, and than deallocated, only to be allocated again. In option 2 the buffer is allocated only once. So, second point: copy-constructors help to generate more efficient code.
Reason 3. Correctness
Now, let's consider above broken string. While it is wrong on many levels, one particularly bad piece of code is it's assignment operator. It deletes original buffer, and than allocates a new one, copying data from rhs
. Two major issues are that if rhs
is the same as this
, there will be nothing to copy from after deletion. And the second problem is that if for any reason new allocation throws an exception (run out of memory, for example), we would already loose the the original buffer. While the first problem can be addressed with a check for self-assignment (if (&rhs == this) return *this;
) the second could not be easily addressed in a straightforward way. One can read about how to write assignment operators in different articles, but agreed approach would be (for this class) somewhere along following lines:
// Rest of BrokenString remains the same
NotSoBrokenString& operator= (const NotSoBrokenString& rhs) {
NotSoBrokenString temp(rhs);
char* const buf = buffer;
buffer = temp.buffer;
temp.buffer = buf;
}
This is much better, since all the allocation is within temp
constructor. If it fails - throws an exception - the original buffer remains valid and usable. (It also addresses the problem of self-assignment - no harm would be done when this
is the same as rhs
, albeit of slight inefficiency).
So, point number three - assignment operators are often done in terms of copy constructors!
Now this answer is becoming quite big, so I won't have an example for this, but often the semantics of the class allows it to be copy-constructed, but not assigned.
Conclusion
The benefits of copy-constructors used over assignment operators include const-correctness, efficiency, code correctness and support for non-trivial semantics.

- 61,605
- 5
- 78
- 137