I've read here that, in a situation where some parent object uniquely owns several children, and each child needs to be able to access its parent, a weak pointer should be used as the back-pointer instead of a shared pointer so as to avoid a dependency cycle.
I have two questions:
What are the advantages of giving a weak pointer to the parent as opposed to a simple reference?
In the code below, how should I pass each child a weak pointer to its parent? I know you can inherit from std::enable_shared_from_this to get a shared pointer from this. Is there an equivalent for weak pointers? Or something else?
#include <memory>
#include <vector>
class Parent;
class Child {
public:
void set_parent(std::weak_ptr<Parent> parent) {
parent_ = parent;
}
std::weak_ptr<Parent> parent_;
};
class Parent {
void add_child(std::unique_ptr<Child> child) {
// child->set_parent(???);
children_.push_back(std::move(child));
}
std::vector<std::unique_ptr<Child>> children_;
};
weak_ptrs come from shared_ptrs. If your parent nodes "uniquely own" the children, then the children cannot have weak_ptrs to their parents, as this would require that a shared_ptr to them exist somewhere.
Note that the example you cite does not state anything about "unique ownership" of the relationship between the items.
Furthermore, so long as the parents never relinquish ownership of the children to code outside of the system, there's no reason to not just have a regular pointer to the parent (it needs to be a pointer because a reference cannot be changed). The pointer will always be valid (from outside of the system), since a parent must exist in order for the child to exist. That's what unique ownership means, after all.
You have said that a parent always uniquely owns a child but you have not specified a few things:
Is it valid to copy a child object?
Is it valid to move a child object?
If you answered yes to either of those then you must consider what happens if a child is copied or moved between parents as your child class wont by default prevent it. So my advice would be to either explicitly delete copy and move constructors and assignment operators OR if you want that ability then the responsibility is on YOU to make sure that if and when a child is copied and or moved that its parent pointer points to the correct parent and to prevent it pointing to an invalid / deleted parent.
If you go down the route of explicitly disabling copy and move semantics then in that circumstance i can't think of anything off the top of my head that would prevent you from using a reference to the parent instead.
Finally if you did actually want weak pointers in the children pointing to their parents then you could consider having your parent class extend from : enable_shared_from_this. I think the typical usecase for this is when you don't know if you will outlive another object but in the case where it is still a valid object then you would like to ensure it stays alive while you perform some operations on it.
Please note if you take that approach the parent object MUST be stored inside a shared_ptr. See shared_from_this. If not then you are in undefined behaviour territory and lets just all agree that is bad.
Edit: i forgot to mention if you can use C++17 you can directly get the weak_ptr from the parent to pass to the child: weak_from_this
Related
is it possible to use c++11 smart pointers with nodes user data? has anyone tried it before?
node->setUserData(void* usrData);
node->getUserData();
Solved:
i figured a way to store objects in a Node without having to delete theme manually(gets automatically deleted when the Node is destroyed) and it might not be the best solution but it is a solution nevertheless, it involves inheriting from cocos2d::Ref class and using nodes setUserObject(Ref* ptr)!
this is how i made it happen:
1)- make a class/struct that inherits from cocos2d::Ref class.
2)- fill it with your custom properties and methods.
3)- make sure the object calls autorelease().
struct Box : public cocos2d::Ref
{
// some properties here
Box() {
//autorelease means let other Nodes retain and release this object.
//if you don't call autorelease then the object destructor won't get called which means in turn object memory is not released.
this->autorelease();
}
~Box() {
//just to check if the object destructor is called or not.
log("=========================");
log("Box is destroyed");
log("=========================");
}
};
4)- make an object instance and put its pointer in any Node UserObject like this:
auto box = new Box;
this->setUserObject(box);// am putting it in the Layer UserObject
now the box object will be destroyed automatically whenever the Layer/Node is destroyed (no delete is needed)
PS: you should properly exit the cocos2d-x app in order for the nodes(Node, Layer, Scene) destructors to be called so can nodes children be destructed properly(children destructors are called) ... just press the back button if your in an emulator or use a close button that calls Director::end().
This was a comment, but got too long.
I wouldn't see it as hopeless as #Joachim Pileborg does in the comments (although his points are true in principle). The only thing you have to assume is that setUserData is only observing, i.e. it performs no memory-related actions and particularly no delete.
You can then either simply use a smart pointer "outside",
auto ptr = std::make_unique<some_object>();
node->setUserData(ptr.get());
Now, if the program then exits in a proper way, at least you don't have to manually delete ptr. But, as mentioned by Joachim, of course you have to make sure that ptr lives as long as it is might be used inside node.
Alternatively, you can write small wrapper around the node class which contains a shared_ptr<void> ptr variable and which offers a setUserData(shared_ptr<void>) method. If this method is invoked, it first copies the shared pointer and internally calls node->setUserData(ptr.get()) method. Then the underlying object is guaranteed to stay alive.
I'm currently trying to find out how to properly use the shared_ptr feature of C++11 in C++ APIs. The main area where I need it is in container classes (Like nodes in a scene graph for example which may contain a list of child nodes and a reference to the parent node and stuff like that). Creating copies of the nodes is not an option and using references or pointers is pain in the ass because no one really knows who is responsible for destructing the nodes (And when someone destructs a node which is still referenced by some other node the program will crash).
So I think using shared_ptr may be a good idea here. Let's take a look at the following simplified example (Which demonstrates a child node which must be connected to a parent node):
#include <memory>
#include <iostream>
using namespace std;
class Parent {};
class Child {
private:
shared_ptr<Parent> parent;
public:
Child(const shared_ptr<Parent>& parent) : parent(parent) {}
Parent& getParent() { return *parent.get(); }
};
int main() {
// Create parent
shared_ptr<Parent> parent(new Parent());
// Create child for the parent
Child child(parent);
// Some other code may need to get the parent from the child again like this:
Parent& p = child.getParent();
...
return 0;
}
This API forces the user to use a shared_ptr for creating the actual connection between the child and the parent. But in other methods I want a more simple API, that's why the getParent() method returns a reference to the parent and not the shared_ptr.
My first question is: Is this a correct usage of shared_ptr? Or is there room for improvement?
My second question is: How do I properly react on null-pointers? Because the getParent method returns a reference the user may think it never can return NULL. But that's wrong because it will return NULL when someone passes a shared pointer containing a null-pointer to the constructor. Actually I don't want null pointers. The parent must always be set. How do I properly handle this? By manually checking the shared pointer in the constructor and throwing an exception when it contains NULL? Or is there a better way? Maybe some sort of non-nullable-shared-pointer?
Using shared pointers for the purpose you describe is reasonable and increasingly common in C++11 libraries.
A few points to note:
On an API, taking a shared_ptr as an argument forces the caller construct a shared_ptr. This is definitely a good move where there is a transfer of ownership of the pointee. In cases where the function merely uses a shared_ptr, it may be acceptable to take a reference to the object or the shared_ptr
You are using shared_ptr<Parent> to hold a back reference to the parent object whilst using one in the other direction. This will create a retain-cycle resulting in objects that never get deleted. In general, used a shared_ptr when referencing from the top down, and a weak_ptr when referencing up. Watch out in particular for delegate/callback/observer objects - these almost always want a weak_ptr to the callee. You also need to take care around lambdas if they are executing asynchronously. A common pattern is to capture a weak_ptr.
Passing shared pointers by reference rather than value is a stylistic point with arguments for and against. Clearly when passing by reference you are not passing ownership (e.g. increasing the reference count on the object). On the other hand, you are also not taking the overhead either. There is a danger that you under reference objects this way. On a more practical level, with a C++11 compiler and standard library, passing by value should result in a move rather than copy construction and be very nearly free anyway. However, passing by reference makes debugging considerably easier as you won't be repeatedly stepping into shared_ptr's constructor.
Construct your shared_ptr with std::make_shared rather than new() and shared_ptr's constructor
shared_ptr<Parent> parent = std::make_shared<Parent>();
With modern compilers and libraries this can save a call to new().
both shared_ptr and weak_ptr can contain NULL - just as any other pointer can. You should always get in the habit of checking before dereferencing and probably assert()ing liberally too. For the constructor case, you can always accept NULL pointers and instead throw at the point of use.
You might consider using a typedef for your shared pointer type. One style that is sometimes used is follows:
typedef std::weak_ptr<Parent> Parent_P;
typedef std::shared_ptr<Parent> Parent_WkP;
typedef std::weak_ptr<Child> Child_P;
typedef std::shared_ptr<Child> Child_WkP;
It's also useful to know that in header files you can forward declare shared_ptr<Type> without having seen a full declaration for Type. This can save a lot of header bloat
The way that you are using shared pointers is correct with 2 caveats.
That your tree of parents and childen must share the lifetime of the pointers with other objects. If your Parent child tree will be the sole users of the pointer, please use a unique_ptr. If another object controls the lifetime of the pointer are you only want to reference the pointer, you may be better off using a weak_ptr unless the lifetime is guaranteed to exceed your Parent Child tree the raw pointer may be suitable.. Please remember that with shared_ptr you can get circular reference so it is not a silver bullet.
As for how to control NULL pointers: well this all comes down to the contract implicit in your API. If the user is not allowed to supply a null pointer, you just need to document this fact. The best way to do this is to include an assert that the pointer is not null. This will crash your application in debug mode (if the pointer is null) but will not incur a runtime penalty on your release binary. If however a null pointer is is an allowed input for some reason, then you need to provide correct error handling in the case of a null pointer.
Children do not own their parents. Rather, it's the other way around. If children need to be able to get their parents, then use a non-owning pointer or reference. Use shared (or better, unique if you can) pointer for parent to child.
Edit
I suppose the code below would assume I have an overloaded version of addChild() that accepts a Sprite already wrapped in a unique_ptr, where taking ownership would be fine. Just thought I'd mention that before someone else did. :) . I made up all the code here after a very long day, so please take it as pseudo code quality meant only to demonstrate the issue at hand.
Original Question
I'm writing a framework where there is a display list, parents/children etc. I'm thinking that using unique_ptr<Sprite> for example is the way to go here, since when you add a child to a parent display object, it's only logical that the parent now becomes the sole owner of that child.
However, there will be methods available such as getChildAt(index) and getChildByName, etc, which I believe should return a reference or pointer value, since these methods are simply meant to expose a child to operations, not transfer ownership.
Finally, the issue and the reason for this question, is in the following situation. Lets assume we have two Sprite objects which are children of the root of display list, Stage. Let's say we have a third child Sprite.
Stage newStage;
std::unique_ptr<Sprite> parentOne(new Sprite);
std::unique_ptr<Sprite> parentTwo(new Sprite);
newStage.addChild(parentOne); //Stage takes ownership of parentOne
newStage.addChild(parentTwo); //Stage takes ownership of parentTwo
std::unique_ptr<Sprite> someChild(new Sprite);
parentOne->addChild(someChild) //parentOne takes ownership of someChild.
Now, somewhere else lets say in the code base of the game or whatever using this framework, someChild is accessed via getChildAt(int index);.
Sprite& child = parentOne->getChildAt(0);
It would be perfectly legal for the following to then happen.
parentTwo->addChild(child);
The addChild method handles removing the child from it's previous parent, if a parent exists, so that the new parent can now make this child part of its section of the display list.
I'm returning the child(ren) from each sprite as a reference or pointer, because we don't want to hand off ownership (in methods such as getChildAt()), just provide access to the child. We don't want to hand it off as a unique_ptr and have it fall out of scope and die.
However, as I say, it would be perfectly legal and normal for this child (now accessed by a reference or pointer) to be passed off to another container (lets say in a drag and drop operation, dragging an item from one list to another). The problem we have now is that the unique ownership needs to be transferred from one parent to another, but we only have a reference or raw pointer.
I'm wondering what a sane solution to this problem would be. If I return a pointer, is it possible to transfer the ownership correctly at this stage?
void Sprite::addChild(Sprite* newChildToOwn)
{
/* by checking newChildToOwn->parent we can see that the child is already owned
by someone else. We need to not only remove the child from that parents' part of the
display list and add it here, but transfer exclusive object ownership of newChildToOwn
to this->.
*/
}
The release method gets you the pointer to the object and releases it from the unique_ptr
http://en.cppreference.com/w/cpp/memory/unique_ptr/release
For your structure, you should probably have method called releaseChild(index) that handles you the ownership by returning pointer.
To transfer ownership you need to have access to the unique_ptr as the raw pointer knows nothing of how it is being used.
Would it work to have the remove child method return the unique_ptr so that the object is kept alive during the transfer and the Sprite will be able to take the ownership? This will allow references to be used in other places as you have already.
Related to this, I would like to know if there are reasons to use pointers to a parent object (not talking about a parent class) instead of references.
Obviously, if the parent could be null, a reference will not work. But what if the parent object creates and destroys the child object in its lifetime so that the child object's pointer back to its parent will never be null? I think it sounds okay in this case, but I have not seen others do it and am still learning a lot about C++.
The specific case I am thinking of is a specialized dialog window that will only be created by a certain window class.
What are some problems (if any) with doing
class Child {
Child(Parent& parent) : parent_(parent) {}
Parent& parent_;
};
versus
class Child {
Child(Parent* parent) : parent_(parent) {}
Parent* parent_;
};
People will argue about it all day long but references are just limited pointers. Don't agonize about which to use.
The advantage of a pointer is you can copy childs, or create a child without a parent, to be given one later. If you need to do this, you have to use a pointer.
If not, use whichever makes you feel warmest inside. It just isn't worth worrying about.
If you make the pointer Parent *const parent_ it is effectively the same as a reference.
here are some differences between references and pointers :
References :
A reference must be initialized at creation time
Once initialized to point to an object, you cant change the reference to point to another object
Pointers :
A pointer does not need to be initialized, you can point to NULL
You can modify your pointer in order to point to another object
Consequences :
Thus depending on the relationship you want to have between child and parents, you will choose pointer or reference. Also prefer reference to pointer if you dont need pointer.
In other words, if child object have the same parent during its lifetime and parent exists at the creation of the child : use reference to point to parent.
I currently have something similar to the following:
class Parent
{
//just a single child... for sake of simplicity
//no other class holds a shared_ptr reference to child
shared_ptr<Child> _child;
System * getSystem() {...}
}
class Child
{
weak_ptr<Parent> _parent;
~Child
{
_parent.lock()->getSystem()->blah();
}
}
The Child destructor always crashes, since when ~Child() runs _parent is always expired. Is there a typical solution to this weirdness?
In short, is there a way to not destroy _parent until ~Child finishes?
Since by the time the destructor for the the child gets called, the parent's destructor has already run (dtors for member objects get run after the dtor for the containing object), even if the child was holding a plain pointer to the parent, calling the parent's member function would be invalid by the time ~Child() was called.
You might be able to work around this by having Child call getSystem() at some earlier point and cache the result. Maybe in the constructor of Child (if it has a reference to the parent at the time) or maybe there can be an interface added so that Parent can let the child know it needs to collect whatever it might need during destruction from the parent at that time.
I understand that neither of these is a great solution (it increases coupling of the objects) -hopefully someone will post a better option.
Removing the circular reference is preferable, but if you cannot you can force Child to be destroyed before Parent is completely gone. In the destructor, explicitly call reset() on Child. This will force it to be destroyed immediately, assuming there are no other shared_ptrs to it.
Warning, if Parent is actually a base class all of it's subclasses will alreayd have been destroyed. Virtual function calls will probably not behave as expected.
First rule of weak_ptr: always check the locking (returned pointer or exception): after all the real reason to use weak_ptr is that it doesn't control the life-cycle of the pointed object.
_parent.lock()->
Here you assume that lock will succeed, IOW that your weak_ptr will not have expired at that time.
So, you should not be using a weak_ptr at all, but a shared_ptr instead.
Things will be much more clear if you do not misuse weak_ptr. You will see that you have two objects that try to manage each other lifetime, and that your design needs to be fixed. (Throwing weak_ptr into the mix does not fix a design.)
Just from the code you posted this should work. The only thing deleting _child is the parent class.
So there are two possibilites: First, something else also has a reference to the _child pointer, and keeps it ref count alive, and then parent is destroyed. Then eventually whatever else is holding onto child is also destroyed, killing off the child then.
Scenario 2 is that the call to getSystem depends on some other members youre not showing us, and those are getting deleted before the _child shared_ptr is.