pidloop
pidloop

Reputation: 123

why is singleton working with pointers but not with references?

I want to write a Meyers Singleton class using references but it does not work. I seem to still get separate instances of the class with each call to the instance accessor method even though a print suggests the constructor is only being called once. But if I write it using pointers it works fine. Can anyone explain why and how to fix the reference version? Thanks!

Pointer version that works:

X.h

class X {
    public:
        static X *getX(void);
        void ipp();
        int geti();
    private:
        X();
        int i;
};

X.cpp

#include "X.h"

X::X()
{
    printf ("X constructor\n");
    i = 0;
}

X *X::getX()
{
    static X the_only_X;
    return &the_only_X;
}

int X::geti()
{
    return (i);
}

void X::ipp()
{
    i++;
}

tryit.cpp

#include <stdio.h>
#include "X.h"

int main (int ac, char *av[])
{
    printf ("main\n");

    X *x1 = X::getX();
    x1->ipp();
    printf ("i= %d\n", x1->geti());

    X *x2 = X::getX();           // same X instance?
    x2->ipp();
    printf ("i= %d\n", x1->geti());
    x1->ipp();
    printf ("i= %d\n", x1->geti());
    x2->ipp();
    printf ("i= %d\n", x1->geti());

    return (0);
}

Build and run, should get i= 1, 2, 3, 4:

g++ -Wall X.cpp tryit.cpp && ./a.out
main
X constructor
i= 1
i= 2
i= 3
i= 4

Reference version that does not work:

X.h:

class X {
    public:
        static X& getX(void);
        void ipp();
        int geti();
    private:
        X();
        int i;
};

X.cpp:

#include "X.h"

X::X()
{
    printf ("X constructor\n");
    i = 0;
}

X& X::getX()
{
    static X the_only_X;
    return the_only_X;
}

int X::geti()
{
    return (i);
}

void X::ipp()
{
    i++;
}

tryit.cpp:

#include <stdio.h>
#include "X.h"

int main (int ac, char *av[])
{
    printf ("main\n");

    X x1 = X::getX();
    x1.ipp();
    printf ("i= %d\n", x1.geti());

    X x2 = X::getX();           // same X instance?
    x2.ipp();
    printf ("i= %d\n", x1.geti());
    x1.ipp();
    printf ("i= %d\n", x1.geti());
    x2.ipp();
    printf ("i= %d\n", x1.geti());

    return (0);
}

Build and run, should get i= 1, 2, 3, 4:

ecd-imac27$ g++ -Wall X.cpp tryit.cpp && ./a.out
main
X constructor
i= 1
i= 1
i= 2
i= 2

Upvotes: 0

Views: 562

Answers (1)

Fred Larson
Fred Larson

Reputation: 62093

X x1 = X::getX(); is making a copy of the singleton instance. So is X x2 = X::getX();. That means you actually have three instances of X. Oops. How can that happen? This is supposed to be a singleton!

If a singleton class is copyable and/or moveable, then it really isn't a singleton. So enforce that by deleting the copy and move operations from your class (which are otherwise provided by the compiler by default):

class X {
    public:
        static X& getX(void);
        void ipp();
        int geti();

    private:
        X();

        X(const X&) = delete;             // Copy ctor
        X(X&&) = delete;                  // Move ctor
        X& operator=(const X&) = delete;  // Copy assignment
        X& operator=(X&&) = delete;       // Move assignment

        int i;
};

Having done this, you will find that X x1 = X::getX(); and X x2 = X::getX(); fail to compile. Good! The compiler is preventing you from making more X instances. Fix that by using references instead:

X& x1 = X::getX();
X& x2 = X::getX();

And all works correctly:

main
X constructor
i= 1
i= 2
i= 3
i= 4

Upvotes: 3

Related Questions