Reputation: 261
It's rather not a question "how to do it" it's rather "how to do it the right way"
I'm developing an editor in Qt where different widgets display the children and its (member) variables. Each of these widgets should hold a reference/pointer to the edited child to display and change their member variables.
The first attempt was the old ANSI C way I learned (and still kinda stuck in) with simple raw pointer to the used objects. It works fine but since the C++11 standard supports smart pointer and using them is recommended I'm trying to use them.
The problem is, I'm not quite sure what's the "best way" to use them in this case... After reading Smart Pointers: Or who owns you baby? and Which kind of pointer do I use when? and a few others I came to different conclusions:
The first is to use a*unique_ptr
since the edited object is clearly the owner which creates and also deletes its children. The widgets are simply referring to the child to show or change them.
The problem is how should the widgets refer to the child...
for now I'm simply still using a raw pointer I got with the get()
method of the unique_ptr
but this seems kinda flawed to me.
I still can accidentaly call delete on the pointer and cancel the benefits of the smart pointer.
The second approach is to use a shared_ptr
because many objects refer to the child and edit it. Also accidentaly deleting it in one widget would do no harm because it is still owned by other objects.
The problem is they own it. When I want to delete it from the edited object I also have to signal all widgets to delete it before it is really gone. (this again seems flawed and error-prone)
I'm not really content with both ways. Is there a clean(er) way to point to the unique_ptr
child of the object? Or am I missing a completely different and better approach to this problem?
Upvotes: 9
Views: 4915
Reputation: 275270
There is a proposal for the world's dumbest smart pointer, which is a non-owning pointer. Basically, it is a T*
that says "oh, and by the way, I don't claim any ownership over this data".
In this case, you have to manage that the observing/passive/dumb pointer gets reset if the unique_ptr
goes away -- half manual lifetime management. Using a raw T*
with a name that indicates it is non-owning works just as well prior to having the above.
There are benefits to doing the above, especially when you have objects with dependent lifetime.
If you do not, then a shared_ptr
and weak_ptr
works. Note, however, that anyone with a weak_ptr
can create a new shared_ptr
, which could extend the lifetime of the shared object beyond the original shared_ptr
. This must happen, as there is otherwise a race condition whereby the user of the weak_ptr
ensures its data is valid, then goes to use it, but before they do the shared_ptr
's data is destroyed.
So the weak_ptr
must either by able to prevent the shared_ptr
from being destroyed (blocking in multi-threaded context, causing it to "fail" somehow in single-threaded contexts). "Fail" in this case consists of extending the lifetime, which solves both the multi-threaded and the single-threaded problem.
(The single threaded problem is that you verify the weak_ptr
is good, do something that causes the shared_ptr
to reset, then continue to want to use the weak_ptr
's data without rechecking. Naively, it could be avoided with exceptions, but upon closer inspection you run into problems where you'd have to code all methods to check for deletion before accessing this
and throw if this
is deleted, a pretty harsh requirement!)
Upvotes: 1
Reputation: 62777
If you are using Qt, you might consider using the Qt smart pointers instead of std:: smart pointers:
or for QObjects:
QPointer
(or QWeakPointer
, esp in Qt4)or to achieve copy-on-write data sharing, Qt container and QString style:
There are other pointer classes too, but some of the above are most likely to do what you want. Also important, if you have your data in Qt container classes, QStrings
or such, they handle their own memory with copy-on-write semantics, and should generally be passed around as plain values (sometimes as references) instead of pointers.
But most importantly, do not use std::unique_ptr
or std::shared_ptr
with QObjects which have parents, because if the parent deletes the child first, then the std:: pointer will delete it again, crashing the program (other way will work ok, child will notify it's parent that it's deleted). In other words, there's a good chance of subtle bugs if you mix QObjects and std:: pointers, so just don't do it. It's not clear from your question if you are doing it, saying just in case.
Upvotes: 1
Reputation: 27567
You want to use a shared_ptr
in place of your unique_ptr
and weak_ptr
's in place of your raw pointers. This will give exactly what you're after. The weak_ptr
's will not interfere with the edited object's ability to delete the underlying object.
Upvotes: 2
Reputation: 137770
Your use case doesn't translate directly to requirements (what if someone else deletes the widget while you're editing it?) but I'll assume you don't need anything beyond a naked pointer.
The Standard Library does not provide any strict pointer-observer class. Among observer entities:
T *
)T &
)std::reference_wrapper<T>
)std::weak_ptr<T>
)If you want a non-nullable, mutable pointer to a non-managed object, which is a fairly reasonable thing to want, you can roll your own.
But naked pointers aren't that bad. The only difference is that it can be nullptr
, and that it doesn't have a nice, long, explicit name inside namespace std
.
Upvotes: 3