Exercise asking to use default copy constructor - for a deep copy

Requirement: Given the class CharQueue2 below with a std::deque<char> data member. Use the compiler generated copy constructor and assignment operator as a bitwise copy of the deque will perform a deep copy. Assume enqueue method is implemented to add element in the deque.

Question:I wrote the below in main. If I understand correctly, the default copy constructor is able to execute a shallow copy. The exercise is talking about a deep copy though. Is this even possible to do a deep copy if the requirement is to use the default copy constructor? I think what I am missing here is the 'bitwise' copy operation step.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
(…)
//My response:
int main() 
{
    CharQueue2 obj1;
    CharQueue2 obj2;
    obj1.enqueue('a');
    obj2 = obj1;

}
(…)

//CharQueue2 given;
class CharQueue2 
{
	public:
		CharQueue2();
		CharQueue2(size_t size);
		void enqueue(char ch);
		char dequeue();
		bool isEmpty() const;
		void swap(CharQueue2& src);
		size_t capacity() const;
	private:
		std::deque<char> vardeque;
};
  
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_; 
};


Last edited on
Topic archived. No new replies allowed.