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
- performs a shallow copy on all of the non-resource instance variables
- allocates memory for each new resource
- 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.
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.
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).
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.
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.
int
s and float
s, this usually isn't a problem.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:
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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With