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.
Related
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.
It is mentioned in Scott Meyer's book that part of the overhead caused by using shared pointers is that they need virutal function to destroy the pointed object correctly. My question is why? Is this not supposed to be the responsibility of the class of that pointed object to have a virtual destructor?
Is this not supposed to be the reponsibility of the class of that pointed object to have a virtual destructor?
That would be one possible way to design a shared pointer, but std::shared_ptr allows you to do the following, even if Base does not have a virtual destructor:
std::shared_ptr<Base> p { new Derived{} };
It does this by capturing the correct deleter for the argument when the std::shared_ptr is constructed, then calls that when the reference count hits zero rather than just using delete (of course, you can pass your own custom deleter to use instead). This is commonly referred to as type erasure, and this technique is generally implemented using virtual function calls.
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'.
I've got an map shared_ptrs
std::unordered_map<uint64_t, std::shared_ptr<Target>> map;
Is there a way to make them weak_ptrs at some point or do I
have to make something like
std::unordered_map<uint64_t,
std::pair<std::shared_ptr<Target>,
std::weak_ptr<Target>>> map;
and swap them?
Thanks in advance
As people already stated in the comments, you can not do that. A shared_ptr always owns a reference, while a weak_ptr never does. The API of the standard library is explicitly designed in a way that the type tells you whether you currently own a reference or not (and the only thing you should do to access pointees of weak_ptr objects is lock() them, and check the resulting shared_ptr for non-null-ness, so you can (even in a multi-threaded environment) be sure, that you own a reference for yourself while working with the object.
What you could possibly do is have a map of weak_ptr all along, and store a shared_ptr elsewhere as long as you want to keep the object alive. Depending on the design or purpose, the place for the shared_ptr might even be a member variable of the object.
If you use a map of pairs, I would not swap() the pair members, but start with a pair of a shared and a weak ptr referring to the same managed object, and just reset() the shared ptr if it is decided to drop the strong reference, not touching the weak_ptr at that point.
You can always use the weak_ptr or shared_ptr itself as the key in the map. So indeed:
std::map<std::weak_ptr<void>, std::string> information_map;
Would be able to associate strings with any kind of weak ptr, regardless of type. This is because std::less<void*> defines a weak total ordering over all possible pointer values according to the standard.
See also
Advanced Shared Pointer Programming Techniques
How to compare pointers?
Is it legal/proper c++0x to leave an object moved for the purpose of move-construction in a state that can only be destroyed? For instance:
class move_constructible {...};
int main()
{
move_constructible x;
move_constructible y(std::move(x));
// From now on, x can only be destroyed. Any other method will result
// in a fatal error.
}
For the record, I'm trying to wrap in a c++ class a c struct with a pointer member which is always supposed to be pointing to some allocated memory area. All the c library API relies on this assumption. But this requirement prevents to write a truly cheap move constructor, since in order for x to remain a valid object after the move it will need its own allocated memory area. I've written the destructor in such a way that it will first check for NULL pointer before calling the corresponding cleanup function from the c API, so that at least the struct can be safely destroyed after the move.
Yes, the language allows this. In fact it was one of the purposes of move semantics. It is however your responsibility to ensure that no other methods get called and/or provide proper diagnostics. Note, usually you can also use at least the assignment operator to "revive" your variable, such as in the classical example of swapping two values.
See also this question