How the move semantic work for make_unique? - unique-ptr

In the code:
accounts.push_back(make_unique<Checking_Account>("James",1000));
I know this is an unamed temporary Checking_Account object and it will move to a vector, but how the 'move' work? I assume the Checking_Account class does not have a 'move' constructor. The push_back method will call a copy constructor but make_unique is a unique pointer,so it not allow to copy or assign.
The temporary Checking_Account object is R-value reference will call a move constructor, but if I haven't defined a move constructor in any class, the code still run correctly. In my understanding, maybe a move constructor is already defined in the smart pointer class, or compiler will do copy elison which is that the compiler will do move constructor for us, but we will not see this? I am not sure which one is correct.

The implementation of make_unique will new a Checking_Account on the heap using the Checking_Account constructor that best matches Checking_Account("James", 1000). Once the Checking_Account is on the heap, it need never be moved or copied.
A unique_ptr will point to the Checking_Account on the heap, and when the unique_ptr needs to move, it will simply pass that pointer to Checking_Account along to the next unique_ptr, while the original unique_ptr will set its pointer to nullptr.

Related

move semantics with temps allocated with new

I'm just wondering if move semantics are restricted to syntax style B.
More specifically, with style B the object is created on the stack and moved. With style A, the object is created on the heap, but it seems can't be moved.
The question very specifically is, can you use move semantics such that the temp is created with NEW? If so, how?
//move c'tor
A(A&& other) : num(other.num), s(other.s){
other.num = 0;
other.s = nullptr; //dyn alloc obj
}
If you do this, it doesn't work (syntax style A).
A a2(new A("blah")); //error
A a2(move(new A("blah"))); //error
This is ok (syntax style B)
A a2(A("blah")); //uses c'tor once
A a2(move(A("blah"))) //uses c'tor followed by move c'tor
You seem to be confused about a number of things, so I'll try to shed some light. You probably know most of this already.
Move semantics were designed to allow objects to transfer ownership of their data to another object. This was motivated largely to avoid copying from temporary objects that didn't need it.
The move constructor is much like the copy constructor, A(const A&), but only accepts a non-const xvalue of the same type, A(A&&) (think of it as an expiring value). Therefore, this constructor can be invoked when given something like a return value from a function or a variable moved via std::move.
Now, the function std::move in itself is a bit of a misnomer, it doesn't actually do anything. All it does it cast a T or T& into a T&&.
To address your question directly, nothing I've mentioned is specific to where the moving object is located, but it is specific about the types. A("blah") calls the constructor with automatic memory and returns that A. new A("blah") on the other-hand calls the constructor with dynamic memory and returns a pointer to that A, ergo an A*. So your syntax A is not trying to invoke the move constructor, but another constructor like A(A*).
To move from an object referenced by a pointer, all you need to do is derefence and move:
A* a1 = new A("blah");
A a2(std::move(*a1));
There's nothing stopping you from defining a constructor like A(A*), but that shouldn't be needed and isn't recommended for using move semantics.
Also, your syntax B comments are incorrect; both are a value-constructor A("blah") followed by the move-constructor A(A&&); the std::move doesn't add anything here.

interface function getting rvalue pointers to initialize shared_ptr

I have a class exposing through it's interface add function:
void AddObject(Object *o);
Inside the class I maintain the objects in set<shared_ptr<Object>>.
Since I will create shared_ptr from the received pointer I thought to limit the function argument to only rvalue pointers so to make sure that the user will not delete the pointer I use. And so I'll change the function declaration to:
void AddObject(Object* &&o);
so a typical use will be:
AddObject(new Object())
preventing the user to accidentally delete pointer I hold.
I don't want to to use shared_ptr in the interface because the user is not familiar with shared_ptr.
Do you think my suggestion is a good idea?
I think this is a bad idea. I'm sure there is a reason why shared_ptr c-tor that gets a raw pointer is marked explicit instead of using r-value. In my eyes, It's better to teach the users once about smart pointers or at least teach them about using make_shared/make_unique (which are safer and, in the case of make_shared, more efficient, BTW).
BTW, why shared_ptr and not unique_ptr?
Also, why set? Even if you want to make sure you hold each pointer only once and searching a vector each time doesn't look natural enough in your code, I don't see a reason to hold the pointers sorted instead of using unordered_set.
First of all, this approach will not prevent the user from deleting the pointer. Consider this example
auto obj = new Object();
AddObject(std::move(obj));
delete obj;
Secondly, the amount of steps between calling new and the creation of shared_ptr should be as few as possible. If anything happens inside AddObject before it can create a shared_ptr, the object will never get deleted.
The same applies if there are more arguments to AddObject(). If constructing those fails, you will leak memory.
void AddObject(Object* &&o, SomeOtherObject* x);
AddObject(new Object(), xx()); // if xx() throws, memory leak will occur
Ideally you would "wrap" object creating into shared_ptr construction:
void AddObject(std::shared_ptr<Object> o);
AddObject(std::make_shared<Object>());
Either of the following methods may solve your problem.
You may append more comments for AddObject to tell users that delete the pointer they added is not allowed. This is almost enough.
Or, you could also make Object inherits from a base class which has a private destructor and a method named destroyByOwner.

What happens if a object to which a smart pointer is pointing to commits suicide?

I have objects that define their own "right of existence". Hence they have the possibility to commit suicide. What happens to a smart pointer that holds it? Will it be dangling or does the pointer recognises that the object destroyed itself and thus point to NULL automatically?
From outside the object I would use ptr.reset(). But what can I do from inside the object?
Terrible, terrible things will happen. The smart pointer will have no idea what just happened and blindly attempt to destroy the object right on cue. This basically means that you have messed up. Objects owning themselves is highly questionable usually, but if you do own yourself, then you must never be owned externally. This directly includes smart pointers.
Proper ownership semantics means that you must have one, and only one, unambiguous owner. This is even true in the case of e.g. shared_ptr, where the object is effectively owned by the reference count.
A self-owning object must be clear about the fact that it owns itself and it is not to be owned externally.
There is a way to do want you want...
The object must keep itself alive using a shared_ptr to itself. If that is the only shared_ptr, then the object can suicide by decrementing the shared_ptr (Probably not a good idea to call reset() having the shared_ptr destroy itself. A better approach is to move the member shared_ptr into a temporary, so the shared_ptr member getting destroyed is not the same shared_ptr as the one causing destruction)
Then, from outside the objects you can use std::weak_ptr, and these will know when the object is destroyed. (Because there is really a metadata object that survives and contains the state, and the weak_ptr checks this before accessing the object.)
It probably makes sense to inherit enable_shared_from_this for such an object, and then make your constructors private and friend make_shared. That way it's impossible for an object instance to exist that is not managed using the internal shared_ptr.
I would suggest using an intrusive pointer. See: Boost intrusive_ptr.
Your object would need an internal reference count. In your implementation of the member function intrusive_ptr_release you could skip clean-up if your object has already 'comitted suicide'.

Object state after move semantics

I want to understand move semantics and rvalue reference and the object state after the function call.
For example: I expect that caller fills the list and gets to the constructor argument:
typedef std::list<int> IntList;
class IntHolder {
public:
explicit IntHolder(IntList&& l)
: m_h(l)
{}
private:
IntList m_h;
};
IntList a;
a.push_back(1);
IntHolder holder(a);
// ... is 'a' guaranteed empty (not empty) here?
a is guaranteed to not be empty, because it was never passed as an rvalue reference: it is a declared variable, not a compiler generated temporary, and hence cannot be passed as an rvalue reference.
What's happening here is that a temporary IntList gets copy-constructed from a, and that temporary is passed to your constructor. a itself remains intact.
To make things really complicated: even that temporary is not moved! When you use an rvalue reference, it decays to a normal reference, which happens when you say m_h(l) in your initializer list. If it were otherwise, you would not be able to access l from within your constructor. So, the temporary is copied a second time. You can enforce move semantics by replacing m_h(l) with m_h(std::move(l)).
Whenever you try to invoke move semantics, what happens depends on how the class you are using is written. Thus, there are no language guarantees. The only thing a move constructor needs to make sure is that the destructor will not mess up when it is run. It is simply an error to access an object in any way after invoking move semantics on it.

How to delete memory for std::unique_ptr stored in a vector

I have a Windows Phone 8 C# project with a C++ DirectX componenet. In the C++ DirectX project, I have a variable defined in my header file:
std::vector<std::shared_ptr<ParticleRenderer>> m_particleRenderer;
In my C++ file, I add new elements to the vector like this:
m_particleRenderer.push_back( std::unique_ptr<ParticleRenderer>(new ParticleRenderer(m_d3dDevice, m_d3dContext, m_renderTargetView, m_depthStencilView)) );
I want to delete this memory, how do I go about deleting it?
Thank you
The objects that are owned by the shared_ptrs will be deleted (and thus their memory will be freed) when the shared_ptrs are destructed, if there are no other shared_ptrs referring to those objects. In this case each of the shared_ptrs in m_particleRenderer will be destructed when either:
The object that contains m_particleRenderer is destructed (assuming that it is a member of an object).
The shared_ptr is removed from m_particleRenderer.
So, if the object that owns m_particleRenderer still exists but it no longer has any use for the elements in m_particleRenderer, then a simple m_particleRenderer.clear(); will remove all the shared_ptrs from the vector. Each removed shared_ptr will be destructed. If it is the only shared_ptr owning its ParticleRenderer then the ParticleRenderer will also be destructed. Otherwise the ParticleRenderer will continue to exist until the last shared_ptr that owns it is destroyed.

Resources