How to write a class with smart pointer? - c++

Suppose I have a class:
class State {
std::shared_ptr<Graph> _graph;
public:
State():_graph(new Graph){}
};
With regards to rule of three, apparently no need to free _graph in destructor as it is a smart pointer. The question is, do I need to write copy constructor and assignment operator for it?
Considering following:
State s1;
State s2 = s1;
What will happen with the second line?
Looks like it will be s2._graph = s1._graph;, pointer shared, so we are safe?

Default generated copy ctors and assignment operators use the ones provided in the class members.
The shared_ptr copy constructor "shares ownership of the object".
The shared_ptr assignment operator replaces and shares.
If this is the behavior you want, their is no need to explicitly declare the copy ctor and the assignment operator.

Related

can I use std::move with class that doesn't provide a move constructor?

I have a class like this:
class myClass
{
int x[1000];
public:
int &getx(int i)
{
return x[i];
}
}
Please note that I did not provide a move construct here.
if I use the following code:
myClass A;
auto B=std::move(a);
does A moves to B or since I did not provided a move constructor, A is copied to B?
Is there any default move constructor for an object? If yes, how it does work with pointers and dynamically allocated arrays?
Although you did not provide explicit move constructor, the compiler provided implicit move constructor to you.
If the definition of a class X does not explicitly declare a move constructor, one will be implicitly declared as defaulted if and only if
X does not have a user-declared copy constructor, and
X does not have a user-declared copy assignment operator,
X does not have a user-declared move assignment operator,
X does not have a user-declared destructor, and
the move constructor would not be implicitly defined as deleted.
So the answer on your question is: no, A is not copied to B.
It is not clear what you ask in other questions. Please specify more details.
Is there any default move constructor for an object?
Yes in your case. For any type T, a move constructor is implicitly declared only if some conditions are met. More details can be seen at cpperference.com.
If yes, how it does work with pointers and dynamically allocated arrays?
The default implementation will make a shallow copies of pointers. As a consequence, more than one object will point to dynamically allocated arrays. That is going lead to problems. See The Rule of Three for more on the subject.
If you have pointers that point to dynamically allocated arrays, you need to:
Provide an explicitly defined copy constructor that does the right thing with the dynamically allocated arrays. The side effect of this will be that the default move constructor will be implicitly deleted. and/or
Provide an explicitly defined move constructor where you move the ownership of the dynamically allocated arrays appropriately.
Move constructor is generated for your class as you don't define any method which avoid its generation (as user defined destructor or copy constructor)
The auto generated move constructor would move each member. For int[1000] it is equivalent to copy.
If no user-defined move constructors are provided for a class type (struct, class, or union), and all of the following is true:
There are no user-declared copy constructors.
There are no user-declared copy assignment operators.
There are no user-declared move assignment operators.
There are no user-declared destructors
then the compiler will declare a move constructor as a non-explicit inline public member of its class with the signature T::T(T&&).
Quote from cppreference
Basically, yes a default move constructor is created as long as you don't define a copy constructor, copy assignment overload, move assignment overload, or destructor. Otherwise you have to either define the behavior yourself or use:
class_name ( class_name && ) = default;
which will explicitly declare the default version of the move constructor.
Your question is similar to this one. Note that std::move simply is a cast. Your value a will be cast to an rvalue reference, but in your case, the original object will not be plundered or pilfered.
To use an API or an idiom correctly, you have to use it for the right thing. Moving makes most sense for e.g. class objects where the bulk of the object data is allocated on the heap. Such an object is easy to pilfer, and the 'move' can leave the pilfered object in a well-defined state. The canonical example could be e.g. some type of string class with string data on the heap.
In your case, what would you like to happen? Assume a is a local var in some function, i.e. assume a is on the stack. Assume b is a global or file scope or anonymous namespace variable, i.e. not on the stack. What would you like to happen when moving from a to b?
For a string, pilfering makes sense. For your case, pilfering is nonsensical.

How does the move constructor look like if I have a vector (or anything like it) member variable?

The title pretty much sums up my question. In more detail: I know that when I declare a move constructor and a move assignment operator in C++11 I have to "make the other objects variables zero". But how does that work, when my variable is not an array or a simple int or double value, but its a more "complex" type?
In this example I have a Shoplist class with a vector member variable. Do I have to invoke the destructor of the vector class in the move assignment operator and constructor? Or what?
class Shoplist {
public:
Shoplist() :slist(0) {};
Shoplist(const Shoplist& other) :slist(other.slist) {};
Shoplist(Shoplist&& other) :slist(0) {
slist = other.slist;
other.slist.~vector();
}
Shoplist& operator=(const Shoplist& other);
Shoplist& operator=(Shoplist&& other);
~Shoplist() {};
private:
vector<Item> slist;
};
Shoplist& Shoplist::operator=(const Shoplist& other)
{
slist = other.slist;
return *this;
}
Shoplist& Shoplist::operator=(Shoplist&& other)
{
slist = other.slist;
other.slist.~vector();
return *this;
}
Whatever a std::vector needs to do in order to move correctly, will be handled by its own move constructor.
So, assuming you want to move the member, just use that directly:
Shoplist(Shoplist&& other)
: slist(std::move(other.slist))
{}
and
Shoplist& Shoplist::operator=(Shoplist&& other)
{
slist = std::move(other.slist);
return *this;
}
In this case, you could as AndyG points out, just use = default to have the compiler generate exactly the same move ctor and move assignment operator for you.
Note that explicitly destroying the original as you did is definitely absolutely wrong. The other member will be destroyed again when other goes out of scope.
Edit: I did say assuming you want to move the member, because in some cases you might not.
Generally you want to move data members like this if they're logically part of the class, and much cheaper to move than copy. While std::vector is definitely cheaper to move than to copy, if it holds some transient cache or temporary value that isn't logically part of the object's identity or value, you might reasonably choose to discard it.
Implementing copy/move/destructor operations doesn't make sense unless your class is managing a resource. By managing a resource I mean be directly responsible for it's lifetime: explicit creation and destruction. The rule of 0 and The rule of 3/5 stem from this simple ideea.
You might say that your class is managing the slist, but that would be wrong in this context: the std::vector class is directly (and correctly) managing the resources associated with it. If you let our class have implicit cpy/mv ctos/assignment and dtors, they will correctly invoke the corresponding std::vector operations. So you absolutely don't need to explicitly define them. In your case the rule of 0 applies.
I know that when I declare a move constructor and a move assignment
operator in C++11 I have to "make the other objects variables zero"
Well no, not really. The ideea is that when you move from an object (read: move it's resource from an object) then you have to make sure that your object it's left aware that the resource it had is no more under it's ownership (so that, for instance, it doesn't try to release it in it's destructor). In the case of std::vector, it's move ctor would set the pointer it has to the internal buffer to nullptr.
I know that when I declare a move constructor and a move assignment operator in C++11 I have to "make the other objects variables zero"
This is not quite correct. What you must do, is maintain validity of the moved from object. This means that you must satisfy the class invariant.
If you have specified a special invariant for a particular class, that requires you to set member variables to zero, then perhaps such class might have to do so. But this is not a requirement for move in general.
Do I have to invoke the destructor of the vector class in the move assignment operator and constructor?
Definitely not. The destructors of the members will be called when the moved from object is destroyed.
What you would typically do, is move construct/assign each member in the move constructor/assignment operator of the containing object. This is what the implicitly generated special member functions do. Of course, this might not satisfy the class invariant for all classes, and if it doesn't, then you may need to write your own versions of them.
The compiler will implicitly generate the special member functions for you, if you don't try to declare them yourself. Here is a minimal, but correct version of your class:
class Shoplist {
vector<Item> slist;
};
This class is default constructible, movable and copyable.
The move constructor should move member-wise:
Shoplist(Shoplist&& other)
: slist(std::move(other.slist))
{}
Note, that the compiler generates move constructors for you (when possible) by member-wise move, as you would do by hand above.
Move constructors are allowed (but not required) "steal" the contents of the moved-from object. This does not mean that they must "make the other objects variables zero". Moving a primitive type, for instance, is equivalent to copying it. What it does mean is that a move constructor can transfer ownership of data in the heap or free store. In this case, the moved-from object must be modified so that when it is destroyed (which should not happen in the move-constructor), the data it previously owned (before it was transferred) will not be freed.
Vector provides its own move constructor. So all you need to do in order to write a correct move constructor for an object containing a vector is to ensure the correct vector constructor is invoked. This is done by explicitly passing an r-value reference to the sub-object constructor, using std::move:
Shoplist(Shoplist&& other) :slist(std::move(other.slist)) {
//... Constructor body
... But in fact you probably don't need to do this in general. Your copy and move constructors will be correctly auto-generated if you don't declare them and don't declare a destructor. (Following this practice is called the "rule of 0".)
Alternatively, you can force the compiler to auto-generate the move constructor:
Shoplist(Shoplist&& other) = default;

What happens when I make a assignment to object which has copy constructor but no assignment operator?

What happens when I make a assignment to object which has copy constructor but no assignment operator?
Will it call compiler's assignment operator, performing memberwise copy?
All classes have an assignment operator, unless you explicitly delete it (not possible prior to C++11). If you do not supply your own implementation, the compiler will supply one for you.
That is the main reason behind the rule of three: if you have a copy constructor, it is nearly certain that you will need an assignment operator and a destructor as well.
The copy constructor plays no role in assignment, the default assignment operator will be called that'll do a bit-wise copy of built-in type members and call assignment operator on object members of class type.
Yes you'll be accessing the default assignment operator generator by compiler, if you don't provide one.
But in general if a class defines one of the following it should probably explicitly define all three
destructor
copy constructor
copy assignment operator
I don't know what do you mean by assignment ( in compiler's context ). So, Let me try by an example.Let's say we have a class Test;
Test a,c; //default constructor would be called for both.
Test b = a; //copy constructor would be called for b as we are creating that object.
c = b; //assignment operator would be called for c as we are changing content's of c.
So, if class Test contains plain objects then it wouldn't matter if you define OR not compiler would do bit-wise copying for you. But if your class contains pointers, then you should explicitly define your copy constructor, assignment operator and destructor.
Hope I'm clear enough.

Why to use copy constructor C++

As i know, when we assign one object is another default copy constructor will be called.
class class1 obj1;
class class2 obj2;
obj1(obj2); //default copy constructor will be called by compiler
So, when should I write explicitly the copy constructor?
In your case the copy-assignment operator will be called, not the copy-constructor. To call the copy-constructor you would have do to e.g.
class1 obj1;
class1 obj2 = obj1; // Invokes the copy-constructor in obj2
A good idea when to write a copy-constructor (or a copy-assignment operator, or a destructor) you can see by reading about the rule of three. In short, if you have any of a destructor, copy-constructor or copy-assignment operator, then you should probably have all of them.
Also, while the compiler will auto-generate copy-constructor and copy-assignment operator for you if you do not provide your own, you have to remember that those auto-generated function will only do a shallow copy. If you have e.g. pointers to memory you allocate in the object, the auto-generated functions will only copy the actual pointer, and not what it points to. This means that after a copy you have two objects both pointing to the same memory. If you delete the pointer in the destructor, and one of the objects are destructed, the other object will still have its pointer, but it will now point to deleted memory.
You will get a default copy constructor when you don't write one, provided you don't add any of the three or five to your class : destructor, copy or move assignment or constructor.
Sometimes this does the right thing. For example, if you just need a shallow copy, or if the member's corresponding functions fo the right thing, for example smart pointers.
If an object exist prior to the assign, then it does not involve a construction but the assignment operator, signatures are:
T& operator=( T const & ); // from l-value ref
T& operator=( T && ); // from r-value ref, since c++11
A frequent strategy is to write the assignment operator as the idiom "copy and swap" :
T& operator=( T const & o ) {
T val( o ); // you need to write the copy ctor
swap(*this,o); // you need to write the swap
return *this;
}
T& operator=( T && o ) {
T val( std::move(o) ); // you need to write the move ctor
swap(*this,o); // you need to write the swap
return *this;
}
The c++11 version of that strategy
T& operator=( T o ) noexcept { // copy done before the operator that can be noexcept ( swap have to too)
swap(*this,o); // you need to write the swap
return *this;
}
In some cases you will find that the way your objects should be copied is not trivial.
If you consider the class :
class Car {
string BrandName;
int NumberOfPassenger;
}
Then it is clear that when you'll be copying two objects, you'll simply want to copy them member by member. There's nothing special to do here so the defaut copy constructor will work just fine.
But imagine that the class is instead :
class Car {
string BrandName;
int NumberOfPassenger;
Mechanics EngineeringStuff;
}
Here Mechanics is a reference type. What the copy constructor will do is simply copying the reference to the new object, so both cars - car1 and car2 - will share the same EngineeringStuff. But a more natural behaviour would be to allocate manually a new Mechanics object when performing the copy, so the cars don't share the same wheels, motors etc...
More generally, it's usually when you have to deal with reference types or certain kind of business logic that you will need to explicitly write your copy constructor.

Does the default assignment operator call operator= on all members?

And similarly, does the default copy constructor call the copy constructor on all members? For example, if a class has a non-POD member like so:
class A
{
std::string str;
};
...will the default compiler-generated copy constructor and assignment operator work correctly? Will they call the string's copy constructor and operator= or will they just make a bitwise copy of member variable str?
In other words, does having a std::string member mean this class needs a user-implemented copy constructor and assignment operator?
Yes, the compiler-generated one will work correctly.
However, if you implement your own and leave them empty, it won't.
If you're not managing memory and all your members provide correct copying/assignment/destruction, you don't need (and shouldn't) implement your own copy constructor/destructor/assignment operator.
In other words, does having a std::string member mean this class needs a user-implemented copy constructor and assignment operator?
No, the compiler-generated ones will work perfectly.