Default copy constructor in cpp shallow or deep copy?
Asked Answered
E

3

6

Does the default copy-constructor do a shallow or a deep copy in C++?

I am really confused with default copy constructor in cpp, as it does shallow copy or deep copy, as when I did v2=v1; suppose v1={1,2,3}, now if I have done v2[0]=1; It does not get reflected but I heard it does shallow copy, can anybody please explain?

Epileptic answered 18/7, 2018 at 15:40 Comment(6)
Please provide a minimal reproducible example, we have no idea how your class looks likeAdi
C++ really doesn't have the concept of deep or shallow copies, just the concept of semantically correct copies for a particular class.Delaunay
@NeilButterworth Unless academia steps in. Then all bets are off.Bernitabernj
The default copy constructor copies the contents of variables, nothing more, nothing less. If you have a copy constructor, then the code in the copy constructor is executed.Hypothesis
Tightly related: What is the Rule of Three?Nuzzle
More details on how {1,2,3} is being stored in v1 is required to provide a complete answer.Nuzzle
G
10

It doesn't do either. It does a memberwise copy. I.e. it copies all the members of the class using their copy constructors. If those members have copy constructors that do a deep copy then you'll get a deep copy, if they do a shallow copy then you'll get a shallow copy, or they could do something else entirely.

Deep copy and shallow copy are not C++ concepts, instead C++ lets you do a deep or shallow copy as you prefer.

Ghetto answered 18/7, 2018 at 15:46 Comment(2)
Note that this means if your class contains a pointer, the pointer and only the pointer is copied. You now have two objects pointing to the same thing. Depending on the ownership of this pointed-at thing, this may be bad.Nuzzle
A memberwise copy is a shallow copy. A “deep” copy is one where the container object knows stuff about how to properly duplicate its referenced field objects.Antidepressant
H
4

Your question is the wrong question.

First, C++ default copy/assign is memberwise; it recursively copies/assigns based on what the members do.

Well designed C++ types follow a few patterns in what they do when copied/assigned that lines up with 3 different kinds of primitive types; value, reference and pointer semantics.

A value semantics type behaves like a value. This corresponds to your "deep copy" roughly. Modifying one variable that follows value semantics never changes another. std::vector is a value semantics type, as is int, double, std::string and other std container types. Value semantics are a strong pattern, and make reasoning about program behaviour easy. Note that value semantics need not be implemented as values; vector is typically implemented as a triple of pointers, with construction/copying/etc overloaded; but to the user of a vector variable, it is a value. (The small exception is move construction (and swap), where vector guarantees the moved-to vector steals the contents; iterators remain valid but refer to a different vector afterwards)

A reference semantics type behaves like a C++ reference type -- int&. This is not the same as a Java/C# reference. C++ references are aliases; int& a=b; means a is another name for b. Then a=7 makes both a and b equal 7. If you had another reference c, a=c would not rebind a to refer to c; it would change what a referred to to have the same value as what c referred to. Reference semantics is very confusing because Ref a = b; and a = b; do fundamentally different things.

Complex objects with reference semantics are rare.

A pointer semantics is like reference semantics, but assignment rebinds who it points to. Sometimes an additional operation, like &, is needed to create a new pointer. A type with pointer semantics includes std::reference_wrapper, std::string_view, std::shared_ptr<double>, gsl::span<char> and int*.

Typically it is a bad idea to mix members with different semantics in the same type; the rule of 0/3/5 states that the best move/copy/assogn/ctor/dtor is the empty one. Your resource management types use RAII and write those special members, the other types aggregate them. Value semantics types are not used mixed with reference/pointer in order for the composite class to get coherant semantics.

TL;DR: it depends. Read the documentation on your members. Document what semantics your types have so users know what to expect.

Note, however, that reference semantics is incompatible with being stored in a std container.

Handshaker answered 18/7, 2018 at 16:41 Comment(0)
F
0

The implicitly generated copy constructor (and assignment operator) performs a shallow copy.

There is no difference between a shallow copy, and deep copy of a value type.

now if I have done v2[0]=1; It does not get reflected

There is no reason to expect such operation to get reflected unless the type of v2 is a referential type.

Frottage answered 18/7, 2018 at 15:49 Comment(3)
This was probably downvoted because it is unclear to someone unused to C++'s ability to have complex objects with value semantics. To improve I'd advise elaborating about value/reference (and pointer) semantics, how member semantics impacts default semantics of a type, and how that flows back into deep/shallow conxept. Because everything you said is true, but I suspect not useful to the OP as written. Meh, maybe I'll just answer.Handshaker
You said "There is no difference between a shallow copy, and deep copy of a value type", what does it mean? as they share the same memory (shallow copy) as this is the assignment operator.Epileptic
@RobinKhurana It means that there is only one kind of copying of value types - there is no distinction between shallow and deep copy for them. as they share the same memory What share the same memory?Frottage

© 2022 - 2024 — McMap. All rights reserved.