【问题标题】:C++ Singleton template class inheritanceC++ Singleton 模板类继承
【发布时间】:2012-07-08 08:38:33
【问题描述】:

我有一个抽象的单例类。我的目标是任何子类只需要实现 init() 函数就可以了。这是我所做的:

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() 是受保护的,不能从公共静态函数中调用。这个问题有2个解决方案。首先我们可以公开 init() 函数,但我不想公开这个函数。所以这只剩下第二种解决方案,改变子类如下:

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

    protected:

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

这很好用,但我不想要朋友声明,因为其他程序员可能会扩展我的代码并且可能不知道应该添加它。

如果没有朋友声明,还有其他方法可以实现吗?也许是 Andrei Alexandrescu 的作品?

编辑:现在在构造函数中调用 init 函数而不是 instance() 函数。

EDIT2:出于技术原因和兼容性,我需要一个 init() 函数,不能只在构造函数中进行初始化。

强制转换的解决方案有效,但如果我从构造函数调用 init(),则强制转换不再有效。有什么建议吗?

【问题讨论】:

  • 调用instance() 两次会导致init() 被调用两次:这正常吗?
  • template&lt;class&gt; Singleton; 是什么意思?似乎并不要求派生类实际上是单例,所以让派生类像往常一样在其构造函数中执行初始化不是更简单吗? (事实上​​,即使你想要求派生类是单例的,我仍然看不到总是在构造函数之后立即调用的“init”函数的原因。)
  • 首先:您现在不创建实例,其次:bool isInit 会有所帮助,但您在这里所期望的可能是 Create 而不是 Init。目前似乎有点混乱。
  • 在你的“edit2”之后:你不应该从基构造函数调用 init(),因为当基构造函数被调用时,派生对象(即它的成员)还不存在。因此,即使您静态调用 derived::init(),您也无法做任何有用的事情。

标签: c++ templates inheritance singleton friend


【解决方案1】:

你根本不需要这个 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;
        }
};

通过这种方式,您不仅可以删除不必要的内容 - 而且还可以防止每次有人调用 instanse() 时调用 init()...

【讨论】:

    【解决方案2】:

    问题是您没有按照您的意图使用虚拟功能。 i,e 现在没有多态性在起作用。为什么?因为您在直接派生对象上调用 init() 。当您在基类引用或基类指针上调用 init() 时,init() 函数的使用是虚拟的,如下所示。然后调用发生在基类范围内,并且由于 instance() 方法是基类方法,因此调用受保护的方法非常好。

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

    【讨论】:

      【解决方案3】:

      您似乎混合了 2 种类型的多态性:

      1) 如果你想“静态地”调用SubSingleton::init(),即使用 CRTP,你不需要在基类中定义它——但是你真的必须让它可以被基类访问。

      2) 但是如果将 init() 定义为 virtual,则使用动态多态,不需要将 init() 设为 public:

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

      【讨论】:

      • +1 好点,但是您将名称 instance 用于函数模板和其主体中的变量。这不会编译。
      • @Walter 你能参考禁止这个的标准段落吗?据我记得这是允许的。
      • 也许是允许的,但由于名字模棱两可,它真的不会编译
      • @Mare Infinitus ,这里没有歧义,它编译得很好(用MSVC10测试)。
      • @Mare: 被允许并使用 gcc 编译
      猜你喜欢
      • 2012-10-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多