简体   繁体   中英

C++ Singleton template class inheritance

I have an abstract Singleton class. My goal is that any subclasses just have to implement the init() function AND NOTHING ELSE. Here is what I did:

template <typename T>
class Singleton
{
    public: 

        Singleton()
        {
            init();
        }

        static T& instance()
        {
            static T instance;
            return instance;
        }

    protected:
        virtual void init() = 0;   
};

class SubSingleton : public Singleton<SubSingleton>
{
    protected: 
        void init()
        {
            cout << "Init SubSingleton" << endl;
        }
};

This won't compile, since init() is protected and can't be called from a public static function. There are 2 solutions to this problem. First we can make the init() function public, but I don't want to expose this function publicly. So this only leaves the second solution, change the subclass as follows:

class SubSingleton : public Singleton<SubSingleton>
{
    friend class Singleton<SubSingleton>;

    protected:

        void init()
        {
            cout << "Init SubSingleton" << endl;
        }
};

This works perfectly, but I don't want the friend statement, since other programmers might extend my code and might not know that this should be added.

Is there any other way of implementing this without the friend statement? Maybe anything from Andrei Alexandrescu?

EDIT: The init function is now called in the constructor instead of the instance() function.

EDIT2: For technical reasons and compatibility I need an init() function and can't just do the initialization in the constructor.

The solution for casting works, but if I call init() from the constructor the casting doesn't work anymore. Any suggestions?

Problem is that you do not use the virtual function as you intend to use. i,e there no polymorphism in action now. Why? because you call init() on the direct derived object. The use of init() function being virtual is when you invoke init() either on the base-class reference or on base class pointer as given below. Then the invocation happens from the base-class scope and since instance() method is a base class method, calling a protected method from that is perfectly fine.

    static T& instance()
    {
        static T myInstance;
        Singleton<T>& t = myInstance;  // Just define a dummy reference here.
        t.init();
        return myInstance;
    }

You do not need this init() stuff at all. It only makes problems here.

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

class SubSingleton : public Singleton<SubSingleton>
{
    public: 
        SubSingleton()
        {
            cout << "Init SubSingleton" << endl;
        }
};

In this way you not only remove unnecessary stuff - but you also prevent from calling init() every time someone calls instanse()...

It seems that you mix 2 types of polimorphysms:

1) If you want to call SubSingleton::init() "statically", ie using CRTP, you don't need to define it in the base class -- but then you really have to make it accessible for the base class.

2) But if you define init() as virtual, you use dynamic polimorphism, and don't need to make init() public:

static T& instance() 
{ 
  static T instance; 
  static_cast<Singleton<T> &>(instance).init(); 
  return instance; 
} 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM