【问题标题】:Singleton and Abstract base class in C++C++ 中的单例和抽象基类
【发布时间】:2009-07-07 09:38:46
【问题描述】:

最近我遇到了关于实现单例但涉及抽象基类的问题。 假设我们有这样的类层次结构:

class IFoo {...}; // it's ABC
class Foo : public IFoo {...};

我们有如下定义的单例类:

template <typename T>
class Singleton
{
public:
static T* Instance() {
   if (m_instance == NULL) {
      m_instance = new T();
   }
   return m_instance;
}
private:
static T* m_instance;
};

所以如果我想像下面这样使用:IFoo::Instance()-&gt;foo(); 我该怎么办?

如果我这样做:class IFoo : public Singleton&lt;IFoo&gt; {...}; 它将不起作用,因为 Singleton 将调用 IFoo 的 ctor 但 IFoo 是 ABC,因此无法创建。

而且这个:class Foo : public IFoo, public Singleton&lt;Foo&gt; {...}; 也不能工作,因为这样类 IFoo 没有方法 Instance() 的接口,所以调用 IFoo::Instance() 会失败。

有什么想法吗?

【问题讨论】:

标签: c++ singleton


【解决方案1】:

你想使用类似的东西

IFoo my_foo = Singleton<Foo>::Instance();
my_foo->foo();

基本上,您必须使用具体类(在本例中为您的类 Foo)来实例化模板 Singleton,并且鉴于您的 Foo 派生自 IFoo,您可以通过基指针引用它。您不能使用不完整或抽象类直接实例化模板。

【讨论】:

    【解决方案2】:

    你不能这样做。根据设计和定义,IFoo 是一个接口。因此实例数为 0。另一方面,单例类的定义是您有 1 个实例。 0 != 1。

    【讨论】:

      【解决方案3】:

      你总是可以这样做:

      class IFoo {};
      class Foo : public IFoo {};
      
      template <typename T>
      class Singleton
      {
          // ..
      };
      
      typedef Singleton<Foo> FooSingleton;
      
      int main()
      {
          FooSingleton::Instance()->foo();
      
          return 0;
      }
      

      【讨论】:

        【解决方案4】:

        这是我发现的另一种可能的解决方案,效果很好。

        将此添加到单例:

        #ifndef ABSTRACT_CLASS
            static T* D()
            {
                return new T();
            }
        #else
            static T* D()
            {
                return NULL;
            }
        #endif
        
        static T* Instance( T*(*func)() )
        {
            if( !m_instance )
            {
                m_instance = func();
            }
        
            return m_instance;
        }
        
        static T* Instance()
        {
            if( !m_instance )
            {
                m_instance = D();
            }
        
            return m_instance;
        }
        

        确保抽象类在标头中,而实现在源中。

        例如:

        // IFoo.h
        //
        #define ABSTRACT_CLASS
        
        class IFoo
        {
            virtual ~IFoo() {}
        
            virtual void SomeFunc() = 0;
        };
        
        extern IFoo* BuildFoo();
        
        
        // Foo.cpp
        //
        #include "IFoo.h"
        
        class Foo : public IFoo
        {
            Foo() {}
            ~Foo() {}
        
            void SomeFunc() {}
        };
        
        IFoo* BuildFoo() { return new Foo(); }
        

        通过这些添加,您现在可以执行以下操作:

        IFoo::Instance( BuildFoo );
        
        IFoo::Instance()->SomeFunc();
        

        请记住在每个抽象类的标题中#define ABSTRACT_CLASS。

        【讨论】:

          【解决方案5】:

          恼人的元答案是,“你为什么要使用单例?”我还没有找到您真正需要使用它的情况。恕我直言,在现实生活中,它的缺点大于优点。

          使用类似 'boost::noncopyable' 的东西可能是你所追求的。

          See this post for more info

          【讨论】:

          • 我同意你的观点。但是你仍然需要学习如何去做(并且正确地去做),这样你才能知道它增加的问题(紧密耦合)。
          【解决方案6】:

          我最近遇到了同样的问题。

          它可以用我所知的gem singleton 来实现。它使用assert 强制唯一性和Curiously recurring template pattern 通过单例调用接口实现:

          template <typename T>
          class Singleton {
           public:
            Singleton(const Singleton<T>&) = delete;
            Singleton& operator=(const Singleton<T>&) = delete;       
            Singleton() {
              assert(!msSingleton);
              msSingleton = static_cast<T*>(this);
            }
            ~Singleton(void) {
              assert(msSingleton);
              msSingleton = 0;
            }
            static T& getSingleton(void) {
              assert(msSingleton);
              return (*msSingleton);
            }
           protected:
            static T* msSingleton; 
          };    
          
          class IFoo : public Singleton<IFoo> {    
           public:
            virtual void foo() = 0;
          };
          
          class FooImpl : public IFoo {
           public:
            FooImpl();
            void foo() override { std::cout << "FooImpl::foo()\n"; }
          };
          
          template <>
          IFoo* Singleton<IFoo>::msSingleton = 0;
          
          FooImpl::FooImpl() { msSingleton = this; }
          

          手动实例化FooImpl后,调用IFoo::getSingleton().foo()会调用FooImpl的代码。

          int main() {
            FooImpl f;
            IFoo::getSingleton().foo();
          }
          

          demo

          【讨论】:

            【解决方案7】:

            这样看:您的程序中没有任何内容可以告诉编译器它应该实例化哪个 IFoo 接口实现。请记住,除了 Foo 之外,可能还有其他实现。

            如果您想通过接口使用类并定义应在其他地方使用哪个实际实现,请查看 Abstract Factory 模式。

            【讨论】:

              【解决方案8】:

              我不得不做一些类似的事情来为一些遗留代码添加单元测试。我不得不替换使用模板的现有单例。我给单例模板提供了两个参数,第一个是接口,第二个是实现。

              但是我还必须添加一个setTestInstance 方法来启用单元测试在运行时覆盖实例。

              template <typename IfaceT, typename ImplT>
              class Singleton
              {
              public:
                 static IfaceT* Instance() {
                    if (m_instance == NULL) {
                       m_instance = new ImplT();
                    }
                    return m_instance;
                 }
              
                 // Only used for unit tests 
                 // Takes ownership of instance
                 static void setTestInstance(IfaceT* instace) {
                    m_instance = instance;
                 }
              private:
                 static IfaceT * m_instance;
              };
              

              在这种情况下,setTestInstance 应该使用std::auto_ptr,而m_instance 应该是boost::scoped_ptr。避免内存泄漏。

              【讨论】:

                【解决方案9】:

                我认为最好的解决方案是在此处引入工厂类或方法。想象一下:

                struct FooCreator
                {
                  typedef IFoo*     result_type;
                
                  result_type operator()()const
                  {
                     return new Foo;
                  }
                };
                
                template<class Factory>
                struct Singleton
                {
                
                  static typename Factory::result_type instance()
                  {
                    if(instance_==typename Factory::result_type())
                      instance_ = Factory()();
                    return instance_;
                  } 
                
                private:
                  Singleton(){};
                
                  static typename Factory::result_type instance_;
                };
                
                template<class F>
                typename F::result_type Singleton<F>::instance_ = typename F::result_type();
                

                最好的问候,
                风向标

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 2015-03-23
                  • 2018-09-19
                  • 1970-01-01
                  • 1970-01-01
                  • 2018-07-11
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多