Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ compiler 'shallow' copies and assignments

I'm taking a class on object oriented programming using C++.

In our text it says,

If we do not declare a copy constructor, the compiler inserts code that implements a shallow copy. If we do not declare an assignment operator, the compiler inserts code that implements a shallow assignment.

What I want to know, is whether this is in fact true, what the alluded to compiler mechanism is actually called, and how it works.

This is not a question about copy constructors, it is about compiler behavior.

EDIT> More context

Copy Constructor as defined by the text:

The definition of a copy constructor contains logic that

  1. performs a shallow copy on all of the non-resource instance variables
  2. allocates memory for each new resource
  3. copies data from the source resource(s) to the newly created resource(s)

Resource as defined by the text

Memory that an object allocates at run-time represents a resource of that object's class.

The management of this resource requires additional logic that was unnecessary for simpler classes that do not access resources. This additional logic ensures proper handling of the resource and is often called deep copying and assignment.

like image 359
bigcodeszzer Avatar asked Oct 22 '15 16:10

bigcodeszzer


People also ask

Does assignment operator do shallow copy?

Assignment OperatorThe default version makes a shallow copy. If a deep copy is desired for assignments on a user-defined type (e.g. a class), then the assignment operator should be overloaded for the class.

What is shallow copy in C?

A shallow copy in this particular context means that you copy "references" (pointers, whatever) to objects, and the backing store of these references or pointers is identical, it's the very same object at the same memory location. A deep copy, in contrast, means that you copy an entire object (struct).

What are problems with shallow copy?

The problem with the shallow copy is that the two objects are not independent. If you modify the one object, the change will be reflected in the other object. A deep copy is a fully independent copy of an object. If we copied our object, we would copy the entire object structure.


3 Answers

It's more accurate to say that the compiler defines a default copy constructor and a default copy assignment operator. These will copy/construct the new object by simply calling the copy constructor for all of the member variables.

  • For primitives like ints and floats, this usually isn't a problem.
  • For pointers, though. This is bad news! What happens when the first object deletes that pointer? Now the other object's pointer is invalid!
  • If a member variable cannot be copied (perhaps you used a std::unique_ptr to fix the above problem), then the default copy assignment/ctor won't work. How can you copy something that can't be copied? This will lead to a compiler error.

If you define your own copy constructor/assignment operator, you can make a "deep copy" instead. You can:

  • Create a new object, rather than copying the pointer
  • Explicitly "shallow copy" a pointer
  • Mix the above two based on what you actually want!
  • Initialize member variables with default/custom values in copied objects rather than copy whatever was in the original object.
  • Disable copying altogether
  • On and on and on

As you can see, there are plenty of reasons why you'd want to implement (or explicitly prohibit) your own copy assignment operator, copy constructor, their move counterparts, and destructor. In fact, there's a well-known C++ idiom known as The Rule of Five (formerly the Rule of 3) that can guide your decision on when to do this.

like image 62
GraphicsMuncher Avatar answered Oct 02 '22 21:10

GraphicsMuncher


Yes it's true, and it's indeed called shallow copying. As for how it works, lets say you have a pointer variable, and you assign it to another pointer variable. This only copies the pointer and not what it points to, this is a shallow copy. A deep copy would have created a new pointer, and copied the actual contents that the first pointer points to.

Something like this:

int* a = new int[10];

// Shallow copying
int* b = a;   // Only copies the pointer a, not what it points to

// Deep copying
int* c = new int[10];
std::copy(a, a + 10, c);  // Copies the contents pointed to by a

The problem with shallow copying in regards to pointers should be quite obvious: After the initialization of b in the above example, you have two pointers both pointing to the same memory. If one then does delete[] a; then both pointers become invalid. If the two pointers are in different objects of some class, then there is no real connection between the pointers, and the second object won't know if the first object have deleted its memory.

like image 29
Some programmer dude Avatar answered Oct 02 '22 21:10

Some programmer dude


The code for shallow copy is a simple assignment for every field. If:

class S {
  T f;
};
S s1, s2;

A assignment like s1=s2; is equivalent to what happens in the following:

class S {
    T f;
  public:
    S &operator=(const S&s) {
      this->f = s.f; // and such for every field, whatever T is
    }
};
S s1, s2;
s1=s2;

This is stated in 12.8-8 of the draft standard:

The implicitly-declared copy constructor for a class X will have the form X::X(const X&) if

— each direct or virtual base class B of X has a copy constructor whose first parameter is of type const B& or const volatile B&, and

— for all the non-static data members of X that are of a class type M (or array thereof), each such class type has a copy constructor whose first parameter is of type const M& or const volatile M&.123

Otherwise, the implicitly-declared copy constructor will have the form X::X(X&)

12.8-28 says:

The implicitly-defined copy/move assignment operator for a non-union class X performs memberwise copy/move assignment of its subobjects. [...] in the order in which they were declared in the class definition.

like image 36
Jean-Baptiste Yunès Avatar answered Oct 02 '22 19:10

Jean-Baptiste Yunès