11

What is a good way to share an instance of an object between several classes in a class hierarchy? I have the following situation:

class texture_manager;

class world {
    ...
    std::vector<object> objects_;
    skybox skybox_;
}

I currently implemented texture_manager as a singleton, and clients call its instancing method from anywhere in the code. texture_manager needs to be used by objects in the objects_ vector, by skybox_, and possibly by other classes as well that may or may not be part of the world class. As I am trying to limit the use of singletons in my code, do you recommend any alternatives to this approach? One solution that came to mind would be to pass a texture_manager reference as an argument to the constructors of all classes that need access to it. Thanks.

Dan Nestor
  • 2,270
  • 1
  • 20
  • 40
  • 1
    Why not just declare a global instance of your texture_manager? Globals are not evil if they're handled correctly. And they're no less or more dangerous in a threaded environment than any other instance passed around to multiple clients... – Mordachai Nov 19 '11 at 23:08

1 Answers1

11

The general answer to that question is to use ::std::shared_ptr. Or if you don't have that, ::std::tr1::shared_ptr, or if you don't have that, ::boost::shared_ptr.

In your particular case, I would recommend one of a few different approaches:

  1. One possibility is, of course, the shared_ptr approach. You basically pass around your pointer to everybody who needs the object, and it's automatically destroyed when none of them need it anymore. Though if your texture manager is going to end up with pointers to the objects pointing at it, you're creating a reference cycle, and that will have to be handled very carefully.

  2. Another possibility is just to declare it as a local variable in main and pass it as a pointer or reference to everybody who needs it. It won't be going away until your program is finished that way, and you shouldn't have to worry about managing the lifetime. A bare pointer or reference is just fine in this case.

  3. A third possibility is one of the sort of vaguely acceptable uses of something sort of like a singleton. And this deserves a detailed explanation.

You make a singleton who's only job is to hand out useful pointers to things. A key feature it has is the ability to tell it what thing to hand out a pointer to. It's kind of like a global configurable factory.

This allows you to escape from the huge testing issues you create with a singleton in general. Just tell it to hand out a pointer to a stub object when it comes time to test things.

It also allows you to escape from the access control/security issue (yes, they create security issues as well) that a singleton represents for the same reason. You can temporarily tell it to pass out a pointer to an object that doesn't allow access to things that the section of code you're about to execute doesn't need access to. This idea is generally referred to as the principle of least authority.

The main reason to use this is that it saves you the problem of figuring out who needs your pointer and handing it to them. This is also the main reason not to use it, thinking that through is good for you. You also introduce the possibility that two things that expected to get the same pointer to a texture manager actually get pointers to a different texture manager because of a control flow you didn't anticipate, which is basically the result of the sloppy thinking that caused you to use the Singleton in the first place. Lastly, Singletons are so awful, that even this more benign use of them makes me itchy.


Personally, in your case, I would recommend approach #2, just creating it on the stack in main and passing in a pointer to wherever it's needed. It will make you think more carefully about the structure of your program, and this sort of object should probably live for your entire program's lifetime anyway.

Omnifarious
  • 50,447
  • 15
  • 117
  • 181
  • 2
    I think using some sort of shared pointer here is just a way of tip-toeing around the tricky (but important) discussion about who actually owns the `texture_manager` object. Should it really get destroyed as soon as nobody has a reference to it anymore? – Frerich Raabe Nov 19 '11 at 22:34
  • 1
    I thought about that, however passing a shared_ptr down the class hierarchy seems cumbersome, and I'm hoping to find an alternate solution. Or is this not what you had in mind? – Dan Nestor Nov 19 '11 at 22:39
  • @FrerichRaabe: Yeah, I just put that in as the first cut of 'standard advice'. It's what the question immediately made me think of. But I analyzed it in a bit more depth and updated my answer. – Omnifarious Nov 19 '11 at 22:44
  • @dandrestor: I updated to provide more options. It was just going to take awhile to type out, and StackOverflow can be a speed contest sometimes. – Omnifarious Nov 19 '11 at 22:45