2012-07-08 189 views
4

我有一个抽象的单例类。我的目标是任何子类都必须实现init()函数AND NOTHING ELSE。以下是我所做的:C++单例模板类继承

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; 
     } 
}; 

由于init()是受保护的,不能从公共静态函数中调用,因此不会编译。这个问题有两种解决方案。首先我们可以公开init()函数,但我不想公开这个函数。因此,这仅留下第二个解决方案,如下更改子类:

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

    protected: 

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

这工作完全,但我不希望朋友声明,因为其他程序员可能会延长我的代码,并且可能不知道,这应该是添加。

有没有其他的方式来实现这个没有朋友的声明?也许有什么来自Andrei Alexandrescu?

编辑:现在在构造函数中调用init函数而不是实例()函数。编辑2:出于技术原因和兼容性我需要一个init()函数,不能只是在构造函数中进行初始化。

铸造工作的解决方案,但如果我从构造函数调用init()铸造不再工作。有什么建议么?

+3

调用'实例()'两次会导致'初始化()'被调用两次:这是正常的吗? – kennytm 2012-07-08 08:44:35

+0

什么是你的模板 Singleton;'?它似乎并不需要派生类实际上是单例,所以让派生类在构造函数中执行它们的初始化并不像通常那样简单吗? (事实上​​,即使你想要派生类是单例,我仍然没有看到在构造函数后总是立即调用的“init”函数的原因。) – 2012-07-08 08:49:12

+0

第一:你现在不创建实例,第二:一个bool isInit会有所帮助,但是你期望这里可能是一个Create而不是一个Init。似乎现在有点混淆了。 – 2012-07-08 08:50:00

回答

3

问题是,你不使用虚拟函数,你打算使用。我现在在行动中没有多态现象。为什么?因为你在直接派生对象上调用init()。使用init()函数是虚拟的,当你在基类引用或基类指针上调用init()时,如下所示。然后调用发生在基类的范围内,因为instance()方法是一个基类方法,所以从中调用一个受保护的方法是非常好的。

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

看来你混的2种polimorphysms的:使用CRTP如果你想SubSingleton::init()“静态”叫

1),也就是说,你不需要在基类来定义它 - 但是那么你真的必须让它可以访问基类。

2)但是,如果你定义的init()作为虚拟的,你使用动态polimorphism,并且不需要进行init()市民:

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

+1好点,但是你使用'instance'作为函数模板和它的主体中的一个变量。这不会编译。 – Walter 2012-07-08 09:10:26

+0

@Walter能否引用标准的paragrhaph来禁止这个?据我记得这是允许的。 – 2012-07-08 09:35:18

+0

也许这是允许的,但它真的不会编译,由于ambigious的名字 – 2012-07-08 10:04:51

3

你根本不需要这个init()的东西。这只会造成问题。

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; 
     } 
}; 

这样,你不仅能去除不必要的东西 - 但你也从调用的init()每次有人打电话instanse()时防止...