3

我正在编写一个实用程序类库,其中许多是单例。我已经使用继承实现了它们:

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

如果您有任何想法,意见等,请告诉我。

谢谢。

4

3 回答 3

6

您的问题是您的模板将在多个编译单元中实例化,因为它是完全内联的。因此,在每个使用模板的编译单元中,您最终都会创建一个单例(每个编译单元)。您需要的是强制全局链接,以便所有编译单元引用相同的模板实例化。即将到来的 C++ 标准将通过extern template支持这一点。您现在可以做的是在您的项目中禁用自动实例化并手动实例化您明确使用的模板。这样,当您在任何编译单元中使用模板时,您将生成对实现的未知引用,然后链接器可以从您进行显式实例化的(一个)编译单元中满足该引用。

于 2009-04-10T23:02:58.217 回答
1

是否有多个线程同时访问 getInstance?这可能会导致创建多个实例。考虑:

  1. 线程1执行“ if (m_instance==0)”并发现它是真的
  2. 线程2执行“ if (m_instance==0)”并发现它是真的
  3. 线程 1 分配一个新的 T
  4. 线程 2 分配一个新的 T

然后其中一个覆盖另一个,并返回一个实例或另一个(取决于编译器优化等)

于 2009-04-10T21:37:22.137 回答
1

您从 Singleton 创建的每个模板类都将有它自己的静态m_instance成员......这些不会在不同的类之间共享,因为当模板被实例化时,它实际上会为每组模板参数生成不同的类。从您进行继承的方式来看,这可能意味着您最终会为从它派生的每个类获得一个 Singleton 实例。也许这是您的问题的原因?

于 2009-04-10T21:43:53.697 回答