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
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
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'的东西可能就是你所追求的。
有关详细信息,请参阅此帖子
#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();
}
#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'的东西可能就是你所追求的。
有关详细信息,请参阅此帖子
#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();
}