Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

c++11 - Ownership and getters

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?

  1. 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?

  2. 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...

  3. 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.

like image 234
cboe Avatar asked Jun 11 '13 21:06

cboe


2 Answers

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.

like image 190
CB Bailey Avatar answered Oct 11 '22 15:10

CB Bailey


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.

like image 27
Andy Prowl Avatar answered Oct 11 '22 16:10

Andy Prowl