What Is a Copy Constructor in C++

What is a copy constructor in C++?

A copy constructor has the following form:

class example 
{
example(const example&)
{
// this is the copy constructor
}
}

The following example shows where it is called.

void foo(example x);

int main(void)
{
example x1; //normal ctor
example x2 = x1; // copy ctor
example x3(x2); // copy ctor

foo(x1); // calls the copy ctor to copy the argument for foo

}

C++ Why was the copy constructor called?

B(A aa) takes an A by value, so when you execute B b(a) the compiler calls the copy constructor A(const A& a) to generate the instance of A named aa in the explicit constructor for B.

The reason you can remove the copy constructor and have this still work is that the compiler will generate a copy constructor for you in cases where you have not also declared a move constructor.

Note: The compiler generated copy constructor is often not sufficient for complex classes, it performs a simple member wise copy, so for complex elements or dynamically allocated memory you should declare your own.

§ 15.8.1

If the class definition does not explicitly declare a copy constructor, a non-explicit one is declared implicitly.
If the class definition declares a move constructor or move assignment operator, the implicitly declared copy
constructor is defined as deleted; otherwise, it is defined as defaulted (11.4). The latter case is deprecated if
the class has a user-declared copy assignment operator or a user-declared destructor or assignment operator.

What's the difference between assignment operator and copy constructor?

A copy constructor is used to initialize a previously uninitialized object from some other object's data.

A(const A& rhs) : data_(rhs.data_) {}

For example:

A aa;
A a = aa; //copy constructor

An assignment operator is used to replace the data of a previously initialized object with some other object's data.

A& operator=(const A& rhs) {data_ = rhs.data_; return *this;}

For example:

A aa;
A a;
a = aa; // assignment operator

You could replace copy construction by default construction plus assignment, but that would be less efficient.

(As a side note: My implementations above are exactly the ones the compiler grants you for free, so it would not make much sense to implement them manually. If you have one of these two, it's likely that you are manually managing some resource. In that case, per The Rule of Three, you'll very likely also need the other one plus a destructor.)

why are copy constructors needed and what are the cases where they are very helpful?

if you pass your objects to methods via pass by copy, c++ must use a copy constructor to generate the instance of that class for the method context. for example, if you store your instances in a vector, when you push back an object it indirectly calls the copy constructor. By the way, for classes c++ generally creates a default copy constructor if there is no explicitly implemented one, this case some times creates unwanted effects (shallow copy).

c++ constructor and copy constructor

What you are seeing is called copy elision in a pre-C++17 compiler (try it out with C++17 on compiler explorer or wandbox with -std=c++17 vs. -std=c++14 flags). As of C++17 the compiler is required to eliminate many cases of copy and move constructors, and construct objects directly without any intermediate objects.

Unlike

A a { 10 };

the line

A a = 10;

means that a temporary object is constructed first, as if the code has:

A a = A(10);

Until C++17, the compiler was allowed to optimize this code, and construct a directly from 10 without the temporary object. Note that the emphasis is that it was allowed but not required to do this copy elision optimization. You have observed this allowed optimization.

The compiler had to compile or fail the code regardless of its decision to do copy elision. If the compiler could not call the copy constructor, like in your case, then it had to fail the compilation unconditionally, even if it decided to do copy elision. This changed with C++17, and the compiler is now required to make the copy elision optimization in this case. Since it is guaranteed to elide the copy constructor, no copy constructor is even required and the code can compile without an error.

Note the copy constructor without const:

A(A &other) { std::cout << " other " << std::endl; value = other.value; }

Without copy elision, this copy constructor can't be used for:

A a = A(10);

It can't be used because A(10) is a temporary object, and as such can be passed as an rvalue parameter to constructors and methods like

A(A && other);
foo(A && other);

or passed as a const lvalue reference parameter to constructors and methods like

A(const A& other);
bar(const A& other);

But it can't be passed as a regular mutable parameter (like in your code block 3).

With copy elision it does not even try to call the copy or the move constructor in these cases.

It still needs to call the copy constructor for

A b = a;

and it can do that with a mutable parameter, only because a is neither a temporary nor a const object. If you make a const then the code will fail to compile, when the copy constructor does not get a const (for C++17 and earlier):

const A a = 10;
A b = a;
// ^^ this will fail

Fun note: The following line will is guaranteed not to call the copy constructor even once with C++17:

A a = A(A(A(A(1))));

Copy Constructor in C++

The types of the member variables is not important for that(1), their semantics are. The rule is simple:

If you don't provide a copy constructor, the compiler will try to generate one for you. This default-generated one will perform the default copy operation on all member variables. For class types, this means calling the copy constructor. For primitive types, this means a bitwise copy.

If the default-generated constructor does what you need, don't declare your own. If it wouldn't do what you need, declare one yourself. It is possible to create a class with non-primitive member variables and perfectly OK default copy semantics:

struct PersonId
{
std::string surname;
std::vector<std::string> givenNames;
};

Likewise, it is possible to create a class with primitive-type member variables where the default copy semantics would not be OK:

class UniqueNamed
{
int id;
UniqueNamed() : id(0) {}

public:
UniqueNamed(const UniqueNamed &src) : id(src.id + 1) {}

int getId() const { return id; }

static UniqueNamed initial;
};

So it depends on the class's semantics, not on the types of its data members.

This touches the general concept of the semantics of copying, moving & wonership, and their implementations in C++. You might want to read something about the rules of zero, three, and five.


(1) Of course, if any member variable is of a non-copyable type, and you want your class to be copyable, you have to provide the copy constructor yourself, as the default-declared one would be defined as deleted.

Copy constructor on C# struct, that works like a C++ copy constructor

Since others have already answered the question about how to call the "copy constructor" (which would be to pass the other instance to the constructor: TestStruct B = new TestStruct(A);), I'll try to answer the other questions.

"Can you have a copy constructor for a C# struct, that works like a C++ copy constructor?"

No. In C++ a copy constructor is automatically called during an assignment of a type to another instance of that type. In C#, a shallow copy of the object is made, meaning that the value of value-type members is copied, and the reference of reference-type members is copied.

From the comments: "Can you overload the assignment operator?"

No. The assignment operator is not something that can be redefined. The default behavior for the language is what happens every time. Of course you can write your own method that returns a new instance of a type based on the property values of another instance, but that method must be called explicitly, and cannot be defined such that it's called during an assignement.

Also from the comments: "I am trying to transparently replace a native value type (int) with a struct or class"

Well now here it sounds like you might have something to work with. Although I'm really not sure what you mean by "replace a native value type", c# does allow you to write your own user-defined conversion operators which allow implicit (and explicit) conversions between two types. This effectively overrides the assignment operator when you're assigning from a different type.

Granted, this is not what your example is doing, but based on your comment it may be useful.

For example, if you wanted to create in instance of your struct from an int, you can do:

struct MyIntReplacement
{
public int MyValue { get; set; }

public static implicit operator MyIntReplacement(int value)
{
return new MyIntReplacement { MyValue = value };
}
}

And now you can do something like:

int someValue = 5;
MyIntReplacement foo = someValue;

C++ understand copy constructor

Also D y(x) is like D y = x

It is not LIKE it, it IS EXACTLY it. The latter is just syntax sugar for the first.

where (like before) C is created (so output C0) and the the copy constructor is called

No, the C copy constructor is not called in that case. You did not define your D copy constructor to call the C copy constructor, so the c member is default-constructed instead, which is why you see C0, but since you did not actually copy c, you don't see Cc.

You are expecting the D copy constructor to automatically call the copy constructors of D's members, but that is simply NOT the case. You have to explicitly define that behavior yourself, eg:

D(const D &src) : c(src.c) { }

Why doesn't the Cc appear?

Because your C copy constructor is not called at all in your D y(x) example.



Related Topics



Leave a reply



Submit