Несколько экземпляров Singleton - PullRequest
3 голосов
/ 11 апреля 2009

Я пишу библиотеку служебных классов, многие из которых являются синглетонами. Я реализовал их как таковые, используя наследование:

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), а также внутри. Например, я использую библиотеку пользовательского интерфейса, не написанную мной, которая отдельно компилируется и к которой я делаю дополнения. Некоторые из этих дополнений используют синглтоны, которые также используются в моей библиотеке .a.

Вызывает ли это отдельный сборник? Что-то еще?

Единственный способ, которым мне удалось обойти эту проблему, - это создать глобальный объект в моем файле main.cpp, который я инициализирую любыми синглетами, которые мне понадобятся. Затем весь код обращается к этому общему глобальному объекту с помощью вызовов, таких как:

GlobalObject::getSomeClass().doSomething()

Ненавижу добавлять дополнительные методы к этому объекту каждый раз, когда создаю еще один синглтон. Плюс синтаксис кажется более понятным и более знакомым при использовании первого метода доступа:

SomeClass::getInstance().doSomething();

Пожалуйста, дайте мне знать, если у вас есть какие-либо мысли, мнения и т. Д.

Спасибо.

Ответы [ 3 ]

5 голосов
/ 11 апреля 2009

Ваша проблема в том, что ваш шаблон будет создан более чем в одном модуле компиляции, поскольку он полностью встроен. Поэтому в каждой единице компиляции, которая использует шаблон, вы в конечном итоге создадите один синглтон (на единицу компиляции). То, что вам нужно, это принудительно установить глобальную связь, чтобы все модули компиляции ссылались на один и тот же экземпляр шаблона. Предстоящий стандарт C ++ будет поддерживать это через extern template . Теперь вы можете отключить автоматическое создание экземпляров в своем проекте и вручную создать шаблоны, которые вы используете явно. Таким образом, когда вы используете шаблон в любом модуле компиляции, вы сгенерируете неизвестную ссылку на реализацию, которая затем может быть удовлетворена компоновщиком из (одного) модуля компиляции, где вы выполняете явное создание экземпляра.

1 голос
/ 11 апреля 2009

Каждый шаблонный класс, который вы создаете из Singleton, будет иметь свой собственный статический m_instance член ... они не разделяются между различными классами, потому что, когда создаются экземпляры шаблонов, он фактически генерирует разные классы для каждого набора параметров шаблона. , Судя по тому, как вы наследуете, это, вероятно, означает, что вы получите экземпляр Singleton для каждого из классов, которые у него наследуются. Возможно, это является причиной вашей проблемы?

1 голос
/ 11 апреля 2009

Имеют ли несколько потоков доступ к getInstance одновременно? Это может привести к созданию нескольких экземпляров. Рассмотрим:

  1. Поток 1 выполняет "if (m_instance==0)" и находит его истинным
  2. Поток 2 выполняет "if (m_instance==0)" и находит его истинным
  3. Поток 1 выделяет новый T
  4. Поток 2 выделяет новый T

Затем один из них перезаписывает другой и возвращает один из экземпляров или другой (в зависимости от оптимизации компилятора и т.

...