Copy elision (NRVO) with deleted copy and move constructors possible? - c++

In the cppreference it is said that
The copy/move constructors need not be present or accessible in a
return statement, when the operand is a prvalue of the same class type
(ignoring cv-qualification) as the function return type:
T f() {
return T();
}
f(); // only one call to default constructor of T
Am I understanding this right and in order to make the above example work, it is inevitable to have at least one copy or move constructor?
What I tried is the following:
class Player
{
//...
public:
Player();
Player(std::string name);
Player& operator=(const Player&) = delete;
Player& operator=(Player&& p) = delete;
Player(const Player& origin) = delete;
Player(Player&& p) = delete;
Player getEmptyPlayer() const {
return Player("Name");
}
}
//in main:
Player p1("It's Me");
Player p2 = p1.getEmptyPlayer();
Why is this code compiling and working? To my understanding actually the method getEmptyPlayer() would need to call the copy constructor for copying the object when returning. Due to efficiency improvements by the compiler this is not the case but the copy elision (NRVO) comes to turn and directly constructs the object where it should be and does not make a copy. Nonetheless, the cppreference (see above) says that an accessible or present copy/move constructor needs to exist. So what is happening here?
Thank you in advance!

Copy elision (NRVO) with deleted copy and move constructors possible?
No.
If there is a copy (or move), then the type must be copyable (or movable). Just because the copy (or move) could be optimised away does not make such program well-formed.
Why is this code compiling and working?
Because there is no copy (nor move) involved (since C++17).
To my understanding actually the method getEmptyPlayer() would need to call the copy constructor for copying the object when returning.
It does not since C++17.
It used to be needed before C++17 (and the call could be elided).

The question absolute nonsense. The cite says "The copy/move constructors need not be present [...]".
I am terribly sorry for not reading correctly.

Related

Why does the compiler require a copying constructor, need and have moving one and doesn't uses any of them?

I've already tried to ask this question but I wasn't clear enough. So here is one more try. And I am very sorry for my English ;)
Let's see the code:
#include <iostream>
#include <memory>
using namespace std;
struct A {
unique_ptr<int> ref;
void printRef() {
if (ref.get())
cout<<"i="<<*ref<<endl;
else
cout<<"i=NULL"<<endl;
}
A(const int i) : ref(new int(i)) {
cout<<"Constructor with ";
printRef();
}
~A() {
cout<<"Destructor with";
printRef();
}
};
int main()
{
A a[2] = { 0, 1 };
return 0;
}
It can not be compiled because unique_ptr has deleted copying constructor.
Orly?!
This class DOES HAVE an implied moving constructor because unique_ptr has one.
Let's do a test:
#include <iostream>
#include <memory>
using namespace std;
struct A {
unique_ptr<int> ref;
void printRef() {
if (ref.get())
cout<<"i="<<*ref<<endl;
else
cout<<"i=NULL"<<endl;
}
A(const int i) : ref(new int(i)) {
cout<<"Constructor with ";
printRef();
}
// Let's add a moving constructor.
A(A&& a) : ref(std::move(a.ref)) {
cout<<"Moving constructor with";
printRef();
}
~A() {
cout<<"Destructor with";
printRef();
}
};
int main()
{
A a[2] = { 0, 1 };
return 0;
}
I've added a moving constructor and now the code can be compiled and executed.
Even if the moving constructor is not used.
The output:
Constructor with i=0
Constructor with i=1
Destructor withi=1
Destructor withi=0
Okay...Let's do one more test and remove the copying constructor (but leave the moving one).
I don't post the code, there only one line has been added:
A(const A& a) = delete;
You should trust me - it works. So the compiler doesn't require a copying constructor.
But it did! (a facepalm should be here)
So what's going on? I see it completely illogical! Or is there some sort of twisted logic I don't see?
Once more:
unique_ptr has a moving constructor and has a deleted copying constructor. Compiler requires copying constructor to be present. But in fact the compiler requires a moving constructor (even if it is not used) and doesn't require a copying (because it could be deleted). And as I see the moving constructor is (should be?) present impliedly.
What's wrong with that?
P.S. One more thing - if I delete the moving constructor the program could not be compiled. So the moving constructor is required, but not the copying one.Why does it require copy-constructor if it's prohibited to use it there?
P.P.S.
Big thanks to juanchopanza's answer! This can be solved by:
A(A&& a) = default;
And also big thanks to Matt McNabb.
As I see it now, the moving constructor is absent because unique_ptr doesn't have a copying one the class has a destructor (and the general rule is that default/copying/moving constructors and destructor could be generated by default only all together). Then the compiler doesn't stop at moving one (why?!) and falls back to copying one. At this point the compiler can't generate it and stops with an error (about the copy constructor) when nothing else can be done.
By the way it you add:
A(A&& a) = delete;
A(const A& a) = default;
It could NOT be compiled with error about 'A::A(A&& a)' deletion, There will be no fall back to copying constructor.
P.P.P.S The last question - why does it stop with error at the COPY constructor but not the MOVE constructor?!
GCC++ 4.7/4.8 says: "error: use of deleted function ‘A::A(const A&)’"
So it stops at copy constructor.
Why?! There should be 'A::A(A&&)'
Ok. Now it seems like a question about move/copy constrcutor choosing rule.
I've created the new more specific question here
This is called copy elision.
The rule in this situation is that a copy/move operation is specified, but the compiler is allowed to optionally elide it as an optimization, even if the copy/move constructor had side-effects.
When copy elision happens, typically the object is created directly in the memory space of the destination; instead of creating a new object and then copy/moving it over to the destination and deleting the first object.
The copy/move constructor still has to actually be present, otherwise we would end up with stupid situations where the code appears to compile, but then fails to compile later when the compiler decides not to do copy-elision. Or the code would work on some compilers and break on other compilers, or if you used different compiler switches.
In your first example you do not declare a copy nor a move constructor. This means that it gets an implicitly-defined copy-constructor.
However, there is a rule that if a class has a user-defined destructor then it does not get an implicitly-defined move constructor. Don't ask me why this rule exists, but it does (see [class.copy]#9 for reference).
Now, the exact wording of the standard is important here. In [class.copy]#13 it says:
A copy/move constructor that is defaulted and not defined as deleted is implicitly defined if it is odr-used (3.2)
[Note: The copy/move constructor is implicitly defined even if the implementation elided its odr-use (3.2, 12.2). —end note
The definition of odr-used is quite complicated, but the gist of it is that if you never attempt to copy the object then it will not try to generate the implicitly-defined copy constructor (and likewise for moving and move).
As T.C. explains on your previous thread though, the act of doing A a[2] = {0, 1}; does specify a copy/move, i.e. the value a[0] must be initialized either by copy or by move, from a temporary A(0). This temporary is able to undergo copy elision, but as I explain earlier, the right constructors must still exist so that the code would work if the compiler decides not to use copy elision in this case.
Since your class does not have a move constructor here, it cannot be moved. But the attempt to bind the temporary to a constructor of A still succeeds because there is a copy-constructor defined (albeit implicitly-defined). At that point, odr-use happens and it attempts to generate the copy-constructor and fails due to the unique_ptr.
In your second example, you provide a move-constructor but no copy-constructor. There is still an implicitly-declared copy-constructor which is not generated until it is odr-used, as before.
But the rules of overload resolution say that if a copy and a move are both possible, then the move constructor is used. So it does not odr-use the copy-constructor in this case and everything is fine.
In the third example, again the move-constructor wins overload resolution so it does not matter what how the copy-constructor is defined.
I think you are asking why this
A a[2] = { 0, 1 };
fails to compile, while you would expect it to compile because A may have a move constructor. But it doesn't.
The reason is that A has a member that is not copyable, so its own copy constructor is deleted, and this counts as a user declared copy constructor has a user-declared destructor.
This in turn means A has no implicitly declared move constructor. You have to enable move construction, which you can do by defaulting the constructor:
A(A&&) = default;
To check whether a class is move constructible, you can use is_move_constructible, from the type_traits header:
std::cout << std::boolalpha;
std::cout << std::is_move_constructible<A>::value << std::endl;
This outputs false in your case.
The twisted logic is that you are supposed to write programs at a higher abstraction level. If an object has a copy constructor it can be copied, otherwise it cannot. If you tell the compiler this object shall not be copied it will obey you and not cheat. Once you tell it that it can be copied the compiler will try to make the copy as fast as possible, usually by avoiding the copy constructor.
As for the move constructor: It is an optimization. It tends to be faster to move an object from one place to another than to make an exact copy and destroy the old one. This is what move constructors are for. If there is no move constructor the move can still be done with the old fashioned copy and destroy method.

C++ -- Incorrect signature for assignment operator?

I'm struggling to basically understand how the assignment operator works, or perhaps I haven't defined the right one, or I've come upon some C++ gotcha; not sure.
I have a class like this:
class A
{
public:
A();
A(const A &rhs);
//some other ctors
//and functions here
A& operator=(A rhs);
A create_half();
double* m_data;
};
Here is the body of create_half; it appears to be relevant to the situation
A A::create_half(){
//construct a new A
A mya;
//do stuff to mya here,
//and then return the new A
return mya;
}
Here is the copy constructor, at least a shortened version of it. This is the code that is skipped due to standard -- at least if this is what's truly going on.
A::A(const A& rhs):
m_data(new double[1])
{
*m_data = *(rhs.m_data);
//or something similar, essentially
//a deep copy
}
Then in the code I try to write something like this
A a_instance;
//do stuff to a_instance
//this doesn't call any = operator or
//copy c-tor I try to define, why?
A b_instance = A.create_half();
//but this works fine
//the = operator I have def'd is called
A b_instance;
b_instance = A.create_half();
The line that doesn't work as I want it to seems to do a default copy (ie, b_instance.m_data is set to a_instance.m_data) even though neither my copy c-tor now my assignment operator contain this operation.
For the problematic line, I'm not able to step into the assignment operator; that's why I think I have the wrong signature, or something similar.
Any ideas?
EDIT: Sorry for misleading you guys -- no class B exists.
EDIT AGAIN: I've accepted an answer, and I've added some detail to the snippets above to try to elucidate what is really going on.
Since the instance of A created by create_half() isn't bound to a reference, the standard allows elision of the copy constructor, which, critically, allocates new memory for b_instance, and does the deep copy.
That seems pretty bold, to me! I don't think I'll see an answer, since this edit is sometime after the initial posting, but what would be a better design pattern here?
This
A a_instance();
is a function declaration that has no parameters and has return type A.
This statement
B b_instance = A.create_half();
syntactically is invalid. At least it should be written as
B b_instance = A().create_half();
In this statement an object of type B is created from an object of type A. I can say nothing whether this statement is correct because you did not show the class B definition.
This statement is also invalid
b_instance = A.create_half();
There must be
b_instance = A().create_half();
There is used indeed an assignment operator (maybe even the copy assignment operator provided that class B has a conversion constructor or class A has a conversion function) but for class B. You did not show how you defined class B. So again I can say nothing what is wrong with this assignment operator.
As for the signature of the copy assignment operator of class A
A& operator=(A rhs);
then it is valid though it would be better if you declared it as
A& operator=( const A &rhs );
EDIT: As you changed your original post then I need to append my message relative to your question in the following code snippet
//this doesn't call any = operator or
//copy c-tor I try to define, why?
A b_instance = A.create_half();
According to the C++ Standard
31 When certain criteria are met, an implementation is allowed to omit
the copy/move construction of a class object, even if the constructor
selected for the copy/move operation and/or the destructor for the
object have side effects. In such cases, the implementation treats the
source and target of the omitted copy/move operation as simply two
different ways of referring to the same object, and the destruction of
that object occurs at the later of the times when the two objects
would have been destroyed without the optimization.
One of the "certin criteria" is
— when a temporary class object that has not been bound to a reference
(12.2) would be copied/moved to a class object with the same
cv-unqualified type, the copy/move operation can be omitted by
constructing the temporary object directly into the target of the
omitted copy/move

About constructors and assign operators in C++

I simply created a class like this:
class GreatClass
{
public:
GreatClass(){cout<<"Default Constructor Called!\n";}
GreatClass(GreatClass &gc){cout<<"Copy Constructor Called!\n";}
GreatClass(const GreatClass &gc){cout<<"Copy Constructor (CONST) Called!\n";}
~GreatClass(){cout<<"Destructor Called.\n";}
GreatClass& operator=(GreatClass& gc){cout<<"Assign Operator Called!";return gc;}
const GreatClass& operator=(const GreatClass& gc){cout<<"Assign Operator (CONST) Called!";return gc;}
};
GreatClass f(GreatClass gc)
{
return gc;
}
and in main() function, there are two versions:
version #1:
int main()
{
GreatClass g1;
GreatClass G = f(g1);
}
version #2:
int main()
{
GreatClass g1;
f(g1);
}
They all generates the SAME output:
Default Constructor Called!
Copy Constructor Called!
Copy Constructor Called!
Destructor Called.
Destructor Called.
Destructor Called.
I do not understand why there is nothing happening when I'm assigning f(g1) to G. What constructor or operator is called at this point?
Thanks.
Compiler implementations are allowed to elide/remove copy constructor calls in certain cases, the example you specify is a good example use case of such a scenario. Instead of creating a temporary object and then copying it to destination object the object is created directly in the destination object and the copy constructor call is removed out.
This optimization is known as Copy elision through Return value optimization.
Also, with C++11 move semantics through rvalue references might kick in instead of the Copy semantics. Even with move semantics the compilers are still free to apply RVO.

What is the behaviour of compiler generated move constructor?

Does std::is_move_constructible<T>::value == true imply that T has a usable move constructor?
If so, what is the default behaviour of it?
Consider the following case:
struct foo {
int* ptr;
};
int main() {
{
std::cout << std::is_move_constructible<foo>::value << '\n';
foo f;
f.ptr = (int*)12;
foo f2(std::move(f));
std::cout << f.ptr << ' ' << f2.ptr << '\n';
}
return 0;
}
and the output is:
1
0000000C 0000000C
I thought that f.ptr should be nullptr.
So in this case,
Is f2 move constructed ?
If so, shouldn't the rvalue be invalidated?
How can I know if instances of a class can be properly move-constructed (invalidate the old one)?
(I'm using VS11.)
Update
The default behaviour of move constructor is same as a copy constructor, is it correct?
If it's true,
We always expect a move ctor to steal the resources of the moved-from object, while the default one does not behave as expected, so what's the point of having a default move ctor?
How can I know if a class has a custom move constructor (which can be guaranteed to behave properly)?
It seems that foo f2(std::move(f)); calls the copy ctor when I declared one, see:
struct foo {
int* ptr;
foo() {}
foo(const foo& other) {
std::cout << "copy constructed\n";
}
};
int main() {
{
std::cout << std::is_move_constructible<foo>::value << '\n';
foo f;
foo f2(std::move(f));
}
system("pause");
return 0;
}
Now the output is:
1
copy constructed
If foo has a move constructor, then wouldn't foo f2(std::move(f)) call it?
So now my questions is:
How to know if a class has a move ctor, and if it has one, how can I explicitly call it?
What I'm trying to do is…
template<typename T, bool has_move_ctor>
struct MoveAux;
template<typename T>
struct MoveAux<T, true> {
static void doMove(T* dest, T* src) {
new(dest) T(std::move(*src)); //move ctor
}
};
template<typename T>
struct MoveAux<T, false> {
static void doMove(T* dest, T* src) {
new(dest) T(*src); //copy ctor
src->~T();
}
};
template<typename T>
inline doMove(T* dest, T* src) {
MoveAux<T,/*a trait*/>::doMove(dest, src);
}
So I thought std::is_move_constructible<T>::value can be passed to the template, while now I see that this trait only cares if T t(T()) is a valid expression, it may call T::T(const T&).
Now assume that T is a custom class, then I want the above templates to behave like:
If I don't declare a move ctor, I want that template method calls the MoveAux<T,false>::doMove.
If I declared one, I need it calls to MoveAux<T,true>::doMove.
Is it possible to make this work?
does std::is_move_constructible<T>::value == true implies that T has a usable move constructor?
Either a move constructor or a copy constructor. Remember that the operation of copy construction satisfies all the requirements that are placed upon the operation move construction, and some more.
In Standard terms, a MoveConstructible object is one for which the evaluation of the expression:
T u = rv;
makes u equivalent to the value of rv before the construction; the state of rv after being moved-from is unspecified. But since it is unspecified, this means the state could even be identical to the one rv had before being moved from: In other words, u could be a copy of rv.
In fact, the Standard defines the CopyConstructible concept to be a refinement of the MoveConstructible concept (so everything which is CopyConstructible is also MoveConstructible, but not vice versa).
if so, what is the default behaviour of it?
The behavior of an implicitly generated move constructor is to perform a member-wise move of the data members of the type for which it is generated.
Per Parahgraph 12.8/15 of the C++11 Standard:
The implicitly-defined copy/move constructor for a non-union class X performs a memberwise copy/move
of its bases and members. [ Note: brace-or-equal-initializers of non-static data members are ignored. See
also the example in 12.6.2. —end note ]
Moreover:
1 - is f2 move constructed ?
Yes.
2 - if so, shouldn't the rvalue be invalidated?
Moving a pointer is the same as copying it. So no invalidation is going on, neither should it be going on. If you want a move constructor that leaves the moved-from object in a particular state (i.e. sets a pointer data member to nullptr), you have to write your own - or delegate this responsibility to some smart pointer class such as std::unique_ptr.
Notice, that the word "invalidated" is not quite correct here. Move constructors (as well as move assignment operators) are meant to leave the moved-from object in a valid (yet unspecified) state.
In other words, the class invariant needs to be respected - and it should be possible to invoke on a moved-from objects operations that do not have any precondition on its state (usually, destruction and assignment).
does std::is_move_constructible::value == true implies that T has a usable move constructor?
No. It states that you can take an rvalue expression of the object type and construct an object from it. Whether this uses the move constructor or the copy constructor is not relevant to this trait.
is f2 move constructed ?
Yes.
if so, shouldn't the rvalue be invalidated?
No. That's not how movement works.
how can I know if instances of a class can be properly move-constructed(invalidate the old one)?
That is not any definition of "properly move-constructed" that exists. If you want to "invalidate the old one", then you will have to do that yourself.
Move construction generally guarantees nothing about the state of the old object. It will be in a valid but undefined state. Such state very much can be "the same as it was before". Move construction for a pointer is the same as copying the pointer.
If you want to "invalidate" after a move, then you need to write your own move constructor that explicitly does that.
(I'm using VS11)
Then you have no compiler-generated move constructors at all. Not that it would matter, since the move and copy constructors for pointers both do the same thing.
the default behaviour of move constructor is same as a copy
constructor, is it correct? if it's true
No. It's wrong. It's true only for primitives. It's similar to that of copy constructor.
The default generated copy constructor calls the copy constructor of all its members in the declared order
But The default generated move constructor calls the move constructor of all its members in the declared order
Now the next question is, what is the copy/move constructor of the primitives ints floats pointers do?
Answer: They just copy the values (both copy and move constructor)
Note that Visual Studio 2012 / VC++11 does not support compiler generated move constructors; in fact, consider this quote from "C++11 Features in Visual C++ 11" blog post (emphasis mine):
Rvalue references v3.0 adds new rules to automatically generate move
constructors and move assignment operators under certain conditions.
This will not be implemented in VC11, which will continue to follow
VC10's behavior of never automatically generating move
constructors/move assignment operators.
With raw pointers, you have to define move constructors by yourself, manually clearing the old "moved-from" pointer:
class Foo
{
public:
// Move constructor
Foo(Foo&& other)
: m_ptr(other.m_ptr) // copy pointer value
{
// Clear out old "moved-from" pointer, to avoid dangling references
other.m_ptr = nullptr;
}
private:
int* m_ptr;
};
Instead, if you use a smart pointer like std::unique_ptr, move constructor is properly defined, and you can just call std::move:
class Foo
{
public:
// Move constructor
Foo(Foo&& other)
: m_ptr(std::move(other.m_ptr)) // move from other,
// old pointer automatically cleared
{
}
private:
std::unique_ptr<int> m_ptr;
};
With automatically generated move constructors, you don't have to define a custom move constructor explicitly, if member-wise move is OK for you.
n3376 12.8/15
The implicitly-defined copy/move constructor for a non-union class X performs a memberwise copy/move
of its bases and members.
Each base or non-static data
member is copied/moved in the manner appropriate to its type:
— if the member is an array, each element is direct-initialized with the corresponding subobject of x;
— if a member m has rvalue reference type T&&, it is direct-initialized with static_cast(x.m);
— otherwise, the base or member is direct-initialized with the corresponding base or member of x.
if foo has a move constructor, then wouldn't foo f2(std::move(f)) calls it?
You do not get the default move constructor when you supply your copy constructor. Add following line to get it ( and notice the change ).
foo(foo&& ifm)=default;

c++ copy constructor signature : does it matter

My current implementation uses lots of copy constructors with this syntax
MyClass::Myclass(Myclass* my_class)
Is it really (functionnaly) different from
MyClass::MyClass(const MyClass& my_class)
and why?
I was adviced that first solution was not a true copy constructor. However, making the change implies quite a lot of refactoring.
Thanks!!!
It's different in the sense that the first isn't a copy constructor, but a conversion constructor. It converts from a MyClass* to a MyClass.
By definition, a copy-constructor has one of the following signatures:
MyClass(MyClass& my_class)
MyClass(const MyClass& my_class)
//....
//combination of cv-qualifiers and other arguments that have default values
12.8. Copying class objects
2) A non-template constructor for class X is a copy constructor if its
first parameter is of type X&, const X&, volatile X& or const volatile
X&, and either there are no other parameters or else all other
parameters have default arguments (8.3.6).113) [ Example: X::X(const
X&) and X::X(X&,int=1) are copy constructors.
EDIT: you seem to be confusing the two.
Say you have:
struct A
{
A();
A(A* other);
A(const A& other);
};
A a; //uses default constructor
A b(a); //uses copy constructor
A c(&a); //uses conversion constructor
They serve different purposes alltogether.
The first version is not a copy constructor. Simple as that. It's just another constructor.
A copy constructor for a class X must have signature (X &, ...) or (X const &, ...) or (X volatile &, ...) or (X const volatile &, ...), where all arguments but the first have default values if they are present (and it must not be a template).
That said, you should think very carefully about why you're violating the Rule of Zero: Most well-designed classes shouldn't have any user-defined copy-constructor, copy-assignment operator or destructor at all, and instead rely on well-designed members. The fact that your current constructor takes a pointer makes me wonder if your code behaves correctly for something like MyClass x = y; — worth checking.
Certain language constructs call for a copy constructor:
passing by value
returning by value
copy-style initialization (although the copy is often elided in that case)
If the language calls for a copy, and you have provided a copy constructor, then it can be used (assuming the cv-qualifications are OK, of course).
Since you have not provided a copy constructor, you will get the compiler-generated copy constructor instead. This works by copying each data member, even if that's not the right thing to do for your class. For example if your not-a-copy-constructor explicitly does any deep copies or allocates resources, then you need to suppress the compiler-generated copy.
If the compiler-generated copy works for your class, then your not-a-copy-constructor is mostly harmless. I don't think it's a particularly good idea, though:
void some_function(const MyClass &foo);
MyClass *ptr = new MyClass();
const MyClass *ptr2 = ptr;
some_function(ptr); // compiles, but copies *ptr and uses the copy
MyClass bar(ptr2); // doesn't compile -- requires pointer-to-non-const
Even assuming that the compiler-generated copy is no good for your class, making the necessary change need not require a lot of refactoring. Suppose that your not-a-constructor doesn't actually modify the object pointed to by its argument, so after fixing the signature you have:
MyClass::MyClass(const MyClass* my_class) {
// maybe treat null pointer specially
// do stuff here
}
You need:
MyClass::MyClass(const MyClass& my_class) {
do_stuff(my_class);
}
MyClass::MyClass(const MyClass* my_class) {
// maybe treat null pointer specially
do_stuff(*my_class);
}
MyClass::do_stuff(const MyClass& my_class) {
// do stuff here
}
You also need to copy any initializer list from the old constructor to the new one, and modify it for the fact that my_class isn't a pointer in the new one.
Removing the old constructor might require a lot of refactoring, since you have to edit any code that uses it. You don't have to remove the old constructor in order to fix any problems with the default copy constructor, though.
The first example is not a copy constructor. This means that when you provide it, the compiler still provides you with a default copy constructor with signature equivalent to
MyClass(const MyClass& my_class);
If you are doing something special with your constructor, and the compiler provided copy constructor does not follow that logic, you should either implement a copy constructor or find a way to disable it.
Why would I want to use a copy constructor instead of a conversion constructor?
It can be problematic if you give MyClass objects to some code that expect your copy-constructor to be valid.
This is the case for STL containers. For instance, if you use a std::vector<MyClass>, you must be aware that vectors are allowed to move elements around for reallocation using their copy constructors.
The default constructor provided by the compiler will perform a shallow copy, calling copy constructors of every attributes, making simple copies for base type like pointers. If you want some form of deep copy you will have to properly rewrite the copy constructor of MyClass