I'm new to C++ and I have troubles wrapping my head around ownership, specifically with a getter. Here's some example code:
class GameObject {
public:
Transform *transform();
private:
Transform _transform;
};
I guess a raw pointer is unsafe to use as someone could access it later when the object doesn't exist anymore?
So I thought about using a unique_ptr for the transform member, since GameObject is the only one that owns the transform. But I can't return that from the getter, can I? But then again, why would I ever use a unique_ptr in the first place instead of adding it as a member like above?
So why not use a shared_ptr? It just seems wrong to me, I don't want to share ownership, GameObject is the owner and others may access it...
So what is it? A reference? I guess shared_ptr seems the wisest choice, since others could safely keep a reference to transform, but what good is that if the enclosing GameObject got destroyed, rendering the transform useless? I'm probably just thinking about ownership the wrong wrong way here but every way seems wrong to me. Thanks for your help.
It is obvious (or should be obvious) to anyone reading your class definition that GameObject
owns a transform
. You are right that "shared ownership" is not implied or desired. As there is no possibility of ever not being able to obtain a transform
from a GameObject
you don't need something that expresses possible nullness like a pointer (raw or otherwise), so returning a reference (possibly) const
seems the most idiomatic thing to do.
You say that a raw pointer is unsafe but it is no more unsafe than any other direct access method. Any way you provide access to the owned transform object (and not a copy of it) gives the client the chance to take its address and store that beyond the lifetime of the owning GameObject
. It is really up to the client not to do dumb things. There is no way to prevent this absolutely, so you should make your interface simple and clear and hard to inadvertently use incorrectly.
I think what is important is that your design models the fact that the Transform
object is being owned by its GameObject
.
If ownership is not meant to be shared (in other words, if the lifetime of the Transform
object is strictly bound by the lifetime of the one and only GameObject
that owns it), then I would avoid shared_ptr
.
In my opinion, your first choice should be the one you actually picked already: just have a subobject of type Transform
. However, return a reference to it rather than a raw pointer:
class GameObject {
public:
Transform& transform();
private:
Transform _transform;
};
This is the clearest possible way of communicating the fact that you are providing access to, yet not giving ownership of, the _transform
subobject.
Clients do not have to bother with questions such as "when and how should I delete this object? And should I delete it at all?", because your interface is clear about it: no ownership, no responsibilites.
There are reasons, on the other hand, that may prevent you from doing this. One possible reason could be that a GameObject
may or may not own a Transform
object. If this is the case, you may want to use a unique_ptr
instead (and return a raw pointer, which may be null).
Another reason for using a unique_ptr
may be that the construction of the Transform
subobject needs to be deferred because the constructor accepts some values that need to be computed, and could not be provided in the constructor's initialization list.
In general, unless you have a good reason for doing so, prefer the simplest solution and just embed a subobject of type Transform
, giving away a reference.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With