My pet project has come to the point where I should start tracking pointer lifetime, and I'm trying to develop some system for it. Sadly, the popular advice to use smart pointers everywhere does not apply since Qt API itself uses naked pointers on every occasion. So, what I came up with is this:
For everything owned by Qt,
- use pointers naked locally;
- pass them between functions naked too;
- store them as a subclassed
QPointer
that makesisNull()
check before conversion to naked.
For everything cleanly owned by me, use smart pointers as advised. I'm going to go with the
std::
versions here.The case that bothers me. For objects that switch ownership (like widgets added/removed from a layout)
- use, store, pass pointers naked;
delete
them manually when appropriate.
Suggestion, comments, advice? I don't like this scheme much myself.
First, hold things by value where you can. View each use of
new
,make_unique
andmake_shared
with suspicion - you must justify each dynamic object creation. If a sub-object has the same lifetime as the parent, holding by value is a no-brainer. For example:You're passing pointers around, but object ownership is clear and there's no change of ownership. Just because a
QObject
has a parent doesn't mean that the parent "owns" it. If the child is destructed before the parent, the ownership ceases. By using C++ semantics - namely the well-defined order of member construction and destruction - you have full control over child lifetimes and noQObject
parent gets to interfere.If you have non-movable objects that have one owner, use
std::unique_ptr
and move it around. That's the way to pass dynamically createdQObject
s around your own code. You can remove them from the pointer at the point where you make their ownership managed by aQObject
parent, if there is such.If you have objects with shared ownership, where their life should end as soon as possible (vs. when the application terminates, or some long-lived object gets destroyed), use
std::shared_ptr
. Ensure that the pointer outlives the users. For example:This example is perhaps contrived, since in Qt most users of objects watch the object's
destroyed()
signal and react to the objects destruction. But this makes sense if e.g.m_view
was a third-party C API object handle that had no way of tracking the data object's lifetime.If the object's ownership is shared across threads, then the use of
std::shared_ptr
is essential: thedestroyed()
signal is only usable within a single thread. By the time you get informed about object deletion in another thread, it's too late: the object has been already destroyed.Thirdly, when you return instances of dynamically created objects from factory methods, you should return them by a naked pointer: it's clear that a factory creates an object for someone else to manage. If you need exception safety, you can return a
std::unique_ptr
instead.