最近我遇到了关于实现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()
将会失败。
有什么想法吗?
答案 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'的东西可能就是你想要的。
答案 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_ptr
而m_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();
}