The distinction between "shallow" and "deep" copies is not particularly useful for C++ programmers outside of the classroom. Instead, an object that is deep copied is said to have
value semantics, while an object that is shallow copied is said to have
reference semantics.
If an object has reference semantics, we need to concern ourselves with object identity. We must keep track of which particular object we're working with so that when any related object is changed we're aware of it. We use the term "reference semantics" because such objects "refer to" other objects, just like references or pointers.
This is analogous to how changing the source of a shallow copy affects the destination, and vice versa (the destination object refers to the source object).
On the other hand, if an object has value semantics, we don't need to care about object identity. Any object will do: we need not worry about where it came from, or if changing it will affect other objects.
We use the term "value semantics" because such objects act like values. For instance, given an
int x = 0 we're not typically concerned with which particular variable x is - only the value that it stores - in this case, zero.
As a whole, value semantics are much simpler for the programmer to reason about.
The default copy constructor is able to execute a shallow copy. |
The default copy constructor calls the copy constructors of member variables. If all of those member variables exhibit value semantics, you'll get a class with value semantics, for free.
If any of those member variables exhibit reference semantics, you'll get a class with reference semantics.
The punchline is that
std::deque is always deep copied. It exhibits value semantics, so you don't need to write a copy constructor, copy assignment operator, or destructor in your own class. The expert authors of the standard library already took care of this for you, and simplified you code by allowing you to obey the
Rule of Zero.
The
Rule of Zero says:
Only write a copy constructor, copy assignment operator, or destructor for a class that directly manages a resource. For instance, if your class
foohas a plain old
std::deque<int> as a member:
1 2 3 4
|
struct foo
{
std::deque<int> x_;
};
|
Then you should not write a copy constructor. There's no need; the default one is fine.
But if you class directly manages a resource (like a pointer to some memory that you've manually taken responsibility for), then you need to obey the
Rule of Three.
The
Rule of Three says:
If a class requires a user-defined destructor, a user-defined copy constructor, or a user-defined copy assignment operator, it almost certainly requires all three. For example,
bar should obey it:
1 2 3 4 5 6 7
|
class bar
{ // no copy constructor, copy assignment operator, or destructor
// this class exhibits reference semantics.
// modify p_ and you might mess up other objects.
explicit bar(int x) : p_(new int{x}) {}
int* p_;
};
|