开发者

C++: Replacement for Singleton

开发者 https://www.devze.com 2023-03-20 00:16 出处:网络
I use singletons a lot because I hate to pass an object of the main class to member classes in order to allow them to access the main class.

I use singletons a lot because I hate to pass an object of the main class to member classes in order to allow them to access the main class.

Class Foo
{
    A* a;
    B* b;
    C* c;
};

For example in the above example if A,B and C would like to access Foo- I would have to pass the object of Foo each one of them, and store it as their member variable (or give the object in every function call). This works, but it does not feel right and requires more code writing.

Instead, I can make Foo a singleton (of course only if there can be only 1 instance) and then just call Foo::getInstance()->... from A, B and C. And I do not have to pass any objects. I find this very handy.

Now, the problem is that I have a situation where there can be multiple instances of Foo. Obviously I cannot use singleton pattern. But I do not want to pass variables nor store them in member classes. Too much code! :)

As an example, I hate this:

开发者_如何学JAVA
A::A(Foo* foo) : m_foo(foo)
{

}

and this:

void A::someFunc(Foo* foo, int someParam)
{

}

But, I love this:

A::A()
{

}

void A::someFunc(int someParam)
{
    Foo* foo = Foo::getInstance();
}

Is there any other way to do it? Something that resembles the singleton pattern?


This kind of pattern is creating a bunch of circular references, which in general is a code smell. You may want to take a solid look at your design, because the usual way to solve these sort of relationship problems is to create a third class that interacts with both existing classes. Alternately, what wrong with passing along a reference to the containing class if you really need that behavior?


Make your singleton class a template.

template<unsigned int NUMBER>
class Foo
{
  A* a;
  B* b;
  C* c;
};

And use whatever few instances you want. It will still remain singleton, but you can have multiple objects:

Foo<1>;
Foo<2>;
Foo<3>;


I've done this in a few projects. Here's some pseudo-code to give you an idea of how I accomplished it:

static map<string, Foo*> instances;

static Foo* Foo::getInstance(string name)
{
    Foo* inst = NULL;

    lock(instances);
    if(instances.count(name) > 0)
    {
        inst = instances[name];
    }
    else
    {
        inst = new Foo();
        instances[name] = inst;
    }

    unlock(instances);
    return inst;
}

Then you just call Foo::getInstance("instance1") or Foo::getInstance("instance2"), etc. All you have to do is remember a string, kinda nice (I think). I don't know if there's a formal name for this design pattern, if there is someone please tell me so I don't sound so ignorant in the future when describing this.


Singletons are evil, we all agree. But the reason why is often forgotten. It's not because they're essentially a global. Globals are great when they solve the problem. Singletons are problematic in that they couple lifetime, initialization order, and global access.

It sounds like you need a global, so use a global.

A* g_a;
B* g_b;
C* g_c;

Initialize all globals in main before anything else needs to access them.

Or do it smarter.

template< typename T >
T& instance( void );

template< typename T >
void set_instance( T& t );

void needs_an_A( void )
{
   instance<A>().a_stuff();
}

Or best of all tie it all in with RAII:

void needs_a_b( void )
{
   B& b = instance<B>();
   b.stuff();
   b.more_stuff();
}

int main()
{
   Initializer<A> init_a;
   Initializer<B> init_b;  // B needs an A during construction
   Initializer<C> init_c( "C constructor param" );

   needs_a_b();
}
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号