C ++中的Singleton和Abstract基类

时间:2009-07-07 09:38:46

标签: c++ singleton

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

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()->foo();我该怎么办?

如果我这样做:class IFoo : public Singleton<IFoo> {...};它将无法工作,因为Singleton将调用IFoo的ctor,但IFoo是一个ABC,因此无法创建。

而且:class Foo : public IFoo, public Singleton<Foo> {...};也无法正常工作,因为这种类IFoo没有方法Instance()的接口,因此调用IFoo::Instance()将会失败。

有什么想法吗?

9 个答案:

答案 0 :(得分:10)

你想要使用像

这样的东西
IFoo my_foo = Singleton<Foo>::Instance();
my_foo->foo();

基本上你必须使用具体的类(在这种情况下,你的类Foo)实例化模板Singleton,并且假设你的Foo来自IFoo,你可以通过基指针引用它。您不能使用不完整或抽象的类直接实例化模板。

答案 1 :(得分:8)

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

答案 2 :(得分:2)

您可以随时执行以下操作:

class IFoo {};
class Foo : public IFoo {};

template <typename T>
class Singleton
{
    // ..
};

typedef Singleton<Foo> FooSingleton;

int main()
{
    FooSingleton::Instance()->foo();

    return 0;
}

答案 3 :(得分:2)

恼人的元答案是,“你为什么要使用单身人士?”我还没有发现真的需要使用它的情况。恕我直言,它的缺点超过了它在现实生活中的优势。

使用类似'boost :: noncopyable'的东西可能就是你想要的。

See this post for more info

答案 4 :(得分:1)

这是我发现的另一种可行的解决方案。

将此添加到Singleton:

#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 :(得分:0)

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

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

答案 6 :(得分:0)

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

但是我还必须添加一个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_ptrm_instance应该是boost::scoped_ptr。为了避免内存泄漏。

答案 7 :(得分:0)

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

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();

最诚挚的问候,
Ovanes

答案 8 :(得分:0)

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

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

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