C ++中的Singleton和Abstract基类

时间:2021-04-24 06:45:38

Recently I got question on implement Singleton but abstract base class involved. Suppose we have class hierarchy like this:

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

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

we have singleton class defined as follows:

我们有单例类定义如下:

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

So if I want to use like following: IFoo::Instance()->foo(); what should I do?

所以,如果我想使用如下:IFoo :: Instance() - > foo();我该怎么办?

If I do this: class IFoo : public Singleton<IFoo> {...}; it won't work since Singleton will call IFoo's ctor but IFoo is a ABC so can not be created.

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

And this: class Foo : public IFoo, public Singleton<Foo> {...}; can't work too, because this way class IFoo doesn't have the interface for method Instance(), so the call IFoo::Instance() will fail.

这个:类Foo:public IFoo,public Singleton {...};也无法工作,因为这种类IFoo没有方法Instance()的接口,所以调用IFoo :: Instance()会失败。

Any ideas?

9 个解决方案

#1


You'd want to use something like

你想要使用类似的东西

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

Basically you'll have to instantiate the template Singleton using a concrete class (in this case, your class Foo) and given that your Foo derives from IFoo you can refer to it through a base pointer. You cannot directly instantiate a template using an incomplete or abstract class.

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

#2


You can't do this. IFoo is an interface, by design and definition. The number of instances is therefore 0. On the other hand, the definition of a singleton class is that you have 1 instance. 0 != 1.

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

#3


You can always do something like this:

你总是可以这样做:

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

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

typedef Singleton<Foo> FooSingleton;

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

    return 0;
}

#4


The annoying meta-answer is, "why are you using a singleton?" I have yet to find a situation where you really need to use it. IMHO its drawbacks outweigh its advantages, in real life situations that is.

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

Using something like 'boost::noncopyable' might be what you are after.

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

See this post for more info

有关详细信息,请参阅此帖子

#5


Here is another possible solution I found that works nicely.

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

Add this to Singleton:

将此添加到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;
}

Ensure the abstract class is in a header, while the implementations are in sources.

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

For example:

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

With these additions, you can now do the following:

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

IFoo::Instance( BuildFoo );

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

Just remember to #define ABSTRACT_CLASS in the header for every abstract class.

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

#6


Look at it like this: There is nothing in your program that would tell the compiler which implementation of the IFoo interface it should be instantiating. Remember, there could be other implementations besides Foo.

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

If you want to use a class via an interface and define which actual implementation shall be used somewhere else, take a look at the Abstract Factory pattern.

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

#7


I had to do something similar to add unit tests to some legacy code. I had to replace an existing singleton which used a template. I gave two parameters to the singleton template, the first is the interface the second is the implementation.

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

However I also had to add a setTestInstance method to enable the unit tests override the instance at runtime.

但是,我还必须添加一个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;
};

In this case setTestInstance should use a std::auto_ptr and m_instance should be a boost::scoped_ptr. To avoid memory leaks.

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

#8


I think the best solution would be to introduce a factory class or method here. Just imagine the following:

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

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

Best Regards,
Ovanes

最诚挚的问候,Ovanes

#9


I've encountered the same problem recently.

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

It can be implemented with what I know as gem singleton. It using assert for forcing uniqueness and Curiously recurring template pattern for calling interface implementation via singleton:

它可以用我所知的gem singleton实现。它使用assert强制唯一性和奇怪的重复模板模式,通过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; }

After manually instantiating FooImpl, call of IFoo::getSingleton().foo() will call FooImpl's code.

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

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

demo

#1


You'd want to use something like

你想要使用类似的东西

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

Basically you'll have to instantiate the template Singleton using a concrete class (in this case, your class Foo) and given that your Foo derives from IFoo you can refer to it through a base pointer. You cannot directly instantiate a template using an incomplete or abstract class.

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

#2


You can't do this. IFoo is an interface, by design and definition. The number of instances is therefore 0. On the other hand, the definition of a singleton class is that you have 1 instance. 0 != 1.

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

#3


You can always do something like this:

你总是可以这样做:

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

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

typedef Singleton<Foo> FooSingleton;

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

    return 0;
}

#4


The annoying meta-answer is, "why are you using a singleton?" I have yet to find a situation where you really need to use it. IMHO its drawbacks outweigh its advantages, in real life situations that is.

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

Using something like 'boost::noncopyable' might be what you are after.

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

See this post for more info

有关详细信息,请参阅此帖子

#5


Here is another possible solution I found that works nicely.

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

Add this to Singleton:

将此添加到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;
}

Ensure the abstract class is in a header, while the implementations are in sources.

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

For example:

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

With these additions, you can now do the following:

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

IFoo::Instance( BuildFoo );

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

Just remember to #define ABSTRACT_CLASS in the header for every abstract class.

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

#6


Look at it like this: There is nothing in your program that would tell the compiler which implementation of the IFoo interface it should be instantiating. Remember, there could be other implementations besides Foo.

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

If you want to use a class via an interface and define which actual implementation shall be used somewhere else, take a look at the Abstract Factory pattern.

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

#7


I had to do something similar to add unit tests to some legacy code. I had to replace an existing singleton which used a template. I gave two parameters to the singleton template, the first is the interface the second is the implementation.

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

However I also had to add a setTestInstance method to enable the unit tests override the instance at runtime.

但是,我还必须添加一个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;
};

In this case setTestInstance should use a std::auto_ptr and m_instance should be a boost::scoped_ptr. To avoid memory leaks.

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

#8


I think the best solution would be to introduce a factory class or method here. Just imagine the following:

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

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

Best Regards,
Ovanes

最诚挚的问候,Ovanes

#9


I've encountered the same problem recently.

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

It can be implemented with what I know as gem singleton. It using assert for forcing uniqueness and Curiously recurring template pattern for calling interface implementation via singleton:

它可以用我所知的gem singleton实现。它使用assert强制唯一性和奇怪的重复模板模式,通过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; }

After manually instantiating FooImpl, call of IFoo::getSingleton().foo() will call FooImpl's code.

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

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

demo