0
\$\begingroup\$

Is this a good Singleton implementation? Is there anything I should be aware of? If so, how can it be improved?

template <class T>
class Singleton
{
public:
    static T& getInstance()
    {
        static T instance;
        return instance;
    }

protected:
    Singleton() {}
    ~Singleton() {}

private:
    Singleton(Singleton const&);
    void operator=(Singleton const&);
};

Usage example:

#include <stdio.h>

class A : public Singleton<A> {
public:
    A() {
        a = 100;
    }

    int get_a() { return a; }
    void set_a(int v) { a = v;  }
private:
    int a;
};

A& s = A::getInstance();

void free1() {
    printf("%d\n", s.get_a());
}

void free2() {
    s.set_a(200);
}

void free3() {
    printf("--> %d", s.get_a());
}

void main() {
    free1();
    free2();
    free3();
}

Let's consider an alternative to avoid the usage of Singletons, instead having:

class A : public Singleton<A>

let's say it becomes:

class A

and let's say we define a global variable:

A g_s = A();

and then this global variable will be accessed by other files using external, would that bring some sort of benefit?

external A g_s;
\$\endgroup\$
  • \$\begingroup\$ There is no good use case for a singleton in c++. Use a static function which serves a reference to a static object. \$\endgroup\$ – Richard Hodges Mar 24 '17 at 13:10
  • \$\begingroup\$ Not sure what you mean there is no good use case but the reason i've decided to use it is to port a fairly large c project using global variables as private data. Even if the code is blazingly fast the structuring is messy. Could you elaborate on that asseveration? Btw, my question is about improving the current code, not judging whether using Singletons or not \$\endgroup\$ – BPL Mar 24 '17 at 13:20
1
\$\begingroup\$

You cannot really say that it's a good or bad implementation. It depends, but this doesn't look ok to me: class A : public Singleton<A>. You don't need to inherit from it to use it: Singleton<A>::instance()

If you want to to use it as you wrote: A& s = A::getInstance(); then probably A's ctor/dtor shouldn't be public.

\$\endgroup\$
  • \$\begingroup\$ What do you mean i don't to inherit from it to use it. Could you please elaborate? \$\endgroup\$ – BPL Mar 24 '17 at 11:15
  • \$\begingroup\$ You do class A : public Singleton<A>, that inheritance is something that I wouldn't do. If you inherit that way, then perhaps ctor/dtor were meant to be private, this way A would be usable only by singleton interface and nobody else could create it. \$\endgroup\$ – Pavel P Mar 25 '17 at 1:45
1
\$\begingroup\$

Assigning the singleton instance's reference to a file-scope static variable is un-necessary.

As is deriving from a singleton, which un-necessarily couples the design of A to the concept of a singleton.

I might be inclined to do it this way:

#include <cstdio>

// the concept of using a singleton.
template<class T>
T& use()
{
    static T t_;
    return t_;
}

// implementation not coupled to the singleton concept.    
class A
{
public:
    A()
    {
        a = 100;
    }

    int get_a() { return a; }

    void set_a(int v) { a = v; }

private:
    int a;
};

void free1()
{
    // use<> an A whenever required.
    printf("%d\n", use<A>().get_a());
}

void free2()
{
    use<A>().set_a(200);
}

void free3()
{
    printf("--> %d", use<A>().get_a());
}

int main()
{
    free1();
    free2();
    free3();
}
\$\endgroup\$
  • \$\begingroup\$ Interesting approach... what'd be the benefit of this against the concept of using global variables & then using externs (edited my question)? \$\endgroup\$ – BPL Mar 24 '17 at 13:52
  • \$\begingroup\$ @BPL using this approach, the singleton is created at the moment it's needed. This means that if one singleton depends on another, they will be created (and destroyed) in the correct order. It also means that if you change your mind about the Singleton-ness, you can do so with relatively little refactoring. You simply pass a copy of an A. \$\endgroup\$ – Richard Hodges Mar 24 '17 at 13:57
  • \$\begingroup\$ Another advantage is that although you'll normally use the singleton instance, you can have more than one instance. This is very important for testing, where you can pass along a mock version of the object in order to avoid the usual problems with singletons. \$\endgroup\$ – Rafael Lerm Mar 25 '17 at 3:12
0
\$\begingroup\$

The trouble with templates is that they can potentially be instantiated in multiple compilation units. It is the job of the linker to remove reduce this number not the compiler. So not all linkers are that nice or compliant so you could still end up with multiple instantiations (thus giving you multiple instances not one).

OK. Lets assume your compiler/linker are good and obey the standard.

Shared libraries are beyond the scope of the C++ standard. So if you link together multiple shared libraries where all these references have been resolved your linker may not even notice that it is inheriting multiple instances from different libraries.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.