我正在编写一个实用程序类库,其中许多是单例。我已经使用继承实现了它们:
template <class T>
class Singleton {
public:
T& getInstance() {
if(m_instance == 0) {
m_instance = new T;
}
return m_instance;
}
private:
static T* m_instance;
};
class SomeClass : public Singleton<SomeClass> {
public:
SomeClass() {}
virtual ~SomeClass() {}
void doSomething() {;}
};
显然这是一个简单的例子,而不是一个实际的类。无论如何,我发现使用代码如:
SomeClass::getInstance().doSomething();
将创建SomeClass的多个实例。我想这可能是因为它在我的库(.a)文件之外以及内部使用。例如,我使用的是一个非自己编写的UI库,它是单独编译的,我正在添加它。这些添加中的一些使用单体,这些单体也在我的.a库中使用。
单独的编译会导致这种情况吗?还有别的吗?
我设法绕过这个问题的唯一方法是在main.cpp文件中创建一个全局对象,我用我需要的任何单例初始化它。然后,所有代码都通过以下调用访问此公共全局对象:
GlobalObject::getSomeClass().doSomething()
我讨厌每次创建另一个单例时都要为这个对象添加一个额外的方法。使用第一种访问方法,语法似乎更清晰,更熟悉:
SomeClass::getInstance().doSomething();
如果您有任何想法,意见等,请告诉我。
感谢。
答案 0 :(得分:5)
您的问题是您的模板将在多个编译单元中实例化,因为它完全是内联的。因此,在每个使用该模板的编译单元中,您最终将创建一个单例(每个编译单元)。您需要的是强制全局链接,以便所有编译单元引用相同的模板实例。即将推出的C ++标准将通过extern template支持此功能。您现在可以做的是禁用项目中的自动实例化并手动实例化您明确使用的模板。这样,当您在任何编译单元中使用模板时,您将生成对实现的未知引用,然后链接器可以从(一个)编译单元中进行显式实例化。
答案 1 :(得分:1)
多个线程是否同时访问getInstance?这可能会导致创建多个实例。考虑:
if (m_instance==0)
”并发现它为真if (m_instance==0)
”并发现它为真然后其中一个覆盖另一个,并返回其中一个实例或另一个(取决于编译器优化等)
答案 2 :(得分:1)
你从Singleton创建的每个模板类都将拥有它自己的静态m_instance
成员......这些不在不同的类之间共享,因为在实例化模板时,它实际上为每组创建了不同的类模板参数。根据你继承的方式来判断,这可能意味着你最终会得到一个Singleton实例,用于从它派生的每个类。也许这是你问题的原因?