Frage

I'm trying to write a simple event manager class and listeners for a game engine. In the usual implementation (i.e. McShaffry) the event manager registers listeners which in principle saves a shared_ptr to the listener as a private member.

I have seen in many instances people saying that shared_ptr and the likes should be avoided (eg here). Thus, I'm trying to find ways to implement the event manager without sharing ownership of the listeners.

One method I've thought of, is assigning unique ids to the listeners and register their ids with the event manager. Then the listeners are responsible of 'asking' the event manager after it has updated, if any events are available under their id.

I would like to ask if there are cleaner and/or standard methods to avoid shared ownership in this case, but also generally. For example, I have the same problem with the listeners. The listeners need to store a pointer to their parent (or the object for which they are listening) so that they can call its methods when handling an event.

War es hilfreich?

Lösung

As Mat’s comment says, there’s no reason not to use smart pointers in general. That said, the cautionary warning does seem to apply in your situation: as far as I understand you don’t have shared ownership; the event manager has sole ownership of the listeners. A shared_ptr would thus be inappropriate here.

An alternative would be to use a unique_ptr which is in many ways the other side of the shared_ptr coin. But depending on how you model listeners even that can be avoided by simply saving concrete instances to the event manager. Without a more detailed description it’s impossible to say whether you need pointers at all but if you don’t need them then, yes, the advice applies: don’t use (smart) pointers when concrete objects would do.

Finally, if your listeners are objects whose ownership is managed elsewhere consider simply using raw pointers to those objects: in that case, the event manager isn’t at all owner of the object – neither the sole nor a shared owner. While this would be the preferred way for me, it requires careful analysis about the listeners’ life-time to ensure that the event manager doesn’t point to listeners which don’t exist any more.

Andere Tipps

shared_ptr tends to be overused; it is often recommended, for example, on SO as a solution to vaguely stated pointer problems. It is not a substitute for good design, and should not be used unless there is a design in place that is based on understanding object lifetime issues in the code being written.

From personal experience, shared_ptrs a great, but sometimes may not be the correct tool for the job. If the code is entirely under your control, 99.9% of the time, shared_ptr will likely make your life easier. You do need to make sure you don't do thinks like:

Foo *f = new Foo();
shared_ptr<Foo> fptr(f);
shared_ptr<Foo> fptr2(f);

This will cause the memory for f to be deallocated with either fptr1 or fptr2. Instead you want to do something like:

Foo *f = new Foo();
shared_ptr<Foo> fptr(f);
shared_ptr<Foo> fptr2 = fptr;

In the second case, the assignment of one shared pointer to another will increment the reference count.

Another place where you can get in trouble with shared_ptr is if you need to pass a naked pointer to a function (this might occur if you need to pass this as the first parameter to a method, or you are relying on a 3rd party library). You can get the naked pointer from the shared_ptr, but you aren't guaranteed the memory address it's pointing to will still be around, as the reference counter won't be incremented.

You can around this by keeping an additional shared_ptr, though this can be a hassle.

There are other forms of smart pointers. For example, OpenSceneGraph has a ref_ptr which is easier to work with than shared_ptr. The one caveat, is that all objects it points to must descend from Referenced. However, if you're okay with that, I think it's a lot more difficult to have really bad things happen.

In some cases shared_ptr is overkill or doesn't properly exibhit the desired semantics (for example passing ownership).

What you need to do is look at your design and see what ownership model you need. If you need/want shared ownership then just use shared_ptr to model that. If a shared/ref counted ownership is not appropriate use another smart pointer.

Wouldn't your case be a good fit for a nice use of auto_ptr described here : http://www.gotw.ca/publications/using_auto_ptr_effectively.htm (guru of the week «using auto_ptr effectively)

For what I understand, you build a listener, then give it to an event manager. So the event manager can be seen as a "sink".

With the auto_ptr technique, your event manager can cleanly and safely take full ownership of the listener you give him.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top