Может ли кто-нибудь предоставить мне образец Singleton в C++?

Я пишу синглтон C++ следующим образом:

class A {
    private:
        static A* m_pA;
        A();
        virtual ~A();

    public:
        static A* GetInstance();
        static void FreeInstance();

        void WORK1();
        void WORK2();
        void WORK3();
    }
}

A* A::GetInstance() {
    if (m_pA == NULL)
        m_pA = new A();
    return m_pA;
}

A::~A() {
    FreeInstance()  // Can I write this? are there any potential error?
}

void A::FreeInstance() {
    delete m_pA;
    m_pA = NULL;
}

Спасибо! Эван Теран и sep61.myopenid.com ответ правильный и действительно хороший! Мой путь неправильный, я бы хотел, чтобы любой, кто пишет такой код, мог избежать моей глупой ошибки.

Мой синглтон A в моем проекте имеет вектор интеллектуального указателя, и другой поток также может редактировать этот вектор, поэтому, когда приложение закрывается, оно всегда становится нестабильным, даже если я добавляю много CMutex. Многопоточная ошибка + одиночная ошибка потеряли мне 1 день.

// ------------------------------------------------ ----------- Новый синглтон, вы можете редактировать его, если считаете, что в следующем примере есть проблемы:

class A {
    private:
        static A* m_pA;
        explicit A();
        void A(const A& a);
        void A(A &a);
        const A& operator=(const A& a);
        virtual ~A();

    public:
        static A* GetInstance();
        static void FreeInstance();

        void WORK1();
        void WORK2();
        void WORK3();
    }
}

A* A::GetInstance() {
    if (m_pA == NULL){
        static A self;
        m_pA = &self;
    }
    return m_pA;
}

A::~A() {
}

10 ответов

Решение

Вы можете избежать необходимости удалять его, используя статический объект, подобный этому:

if(m_pA == 0) {
    static A static_instance;
    m_pA = &static_instance;
}

Почему все хотят вернуть синглтон в качестве указателя?
Вернуть его в качестве ссылки кажется гораздо более логичным!

Вы никогда не сможете освободить синглтон вручную. Как узнать, кто хранит ссылку на синглтон? Если вы не знаете (или не можете гарантировать), что ни у кого нет ссылки (в вашем случае с помощью указателя), тогда вы не имеете права освобождать объект.

Используйте статический метод в методе.
Это гарантирует, что он будет создан и уничтожен только один раз. Это также дает вам ленивую инициализацию бесплатно.

class S
{
    public:
        static S& getInstance()
        {
            static S    instance;
            return instance;
        }
    private:
        S() {}
        S(S const&);              // Don't Implement.
        void operator=(S const&); // Don't implement
 };

Обратите внимание, что вам также нужно сделать конструктор приватным. Также убедитесь, что вы переопределяете конструктор копирования по умолчанию и оператор присваивания, чтобы вы не могли сделать копию синглтона (иначе он не был бы синглтоном).

Также прочитайте:

Чтобы убедиться, что вы используете синглтон по правильным причинам.

Хотя технически не поточно-ориентирован в общем случае см.:
Каково время жизни статической переменной в функции C++?

GCC имеет явный патч для компенсации этого:
http://gcc.gnu.org/ml/gcc-patches/2004-09/msg00265.html

Синглтон в C++ можно записать так:

static A* A::GetInstance() {
    static A sin;
    return &sin;
}

Только не забудьте сделать конструктор копирования и операторы присваивания приватными.

Я не думаю, что есть какая-либо причина, чтобы написать эту строку нет. Ваш метод-деструктор не является статичным, и ваш экземпляр-одиночка не будет уничтожен таким образом. Я не думаю, что деструктор необходим, если вам нужно очистить объект, используйте статический метод, который вы уже создали, FreeInstance().

Кроме этого, вы создаете свои синглеты примерно так же, как я создаю свои.

После периода дикого энтузиазма в отношении синглетов в стиле Мейерса (с использованием локальных статических объектов, как в некоторых предыдущих ответах), я полностью заболел проблемами управления временем жизни в сложных приложениях.

Я склоняюсь к тому, что в конечном итоге вы нарочно ссылаетесь на метод "Экземпляр" на ранней стадии инициализации приложения, чтобы убедиться, что они созданы, когда вы хотите, а затем играете во все виды игр с разбором из-за непредсказуемого (или по крайней мере, очень сложный и несколько скрытый) порядок, в котором вещи разрушаются.

YMMV, конечно, и это немного зависит от природы самого синглтона, но большая часть раздумий об умных синглетонах (и проблемах с потоками / блокировками, которые окружают ум) переоценена IMO.

Если вы прочитаете "Современный дизайн C++", вы поймете, что одноэлементный дизайн может быть намного сложнее, чем возвращать статическую переменную.

Есть отличная библиотека C++, ACE, основанная на шаблонах. Там много документации о различных видах шаблонов, поэтому посмотрите на их работу: http://www.cs.wustl.edu/~schmidt/ACE.html

Эта реализация хороша, если вы можете ответить на следующие вопросы:

  1. Вы знаете, когда объект будет создан (если вы используете статический объект вместо нового? У вас есть main()?)

  2. есть ли у вас синглтон какие-либо зависимости, которые могут быть не готовы к моменту его создания? Если вы используете статический объект вместо нового, какие библиотеки были инициализированы к этому времени? Что ваш объект делает в конструкторе, который может их потребовать?

  3. когда он будет удален?

Использование new() более безопасно, потому что вы контролируете, где и когда объект будет создан и удален. Но тогда вам нужно удалить его явно, и, вероятно, никто в системе не знает, когда это сделать. Вы можете использовать atexit() для этого, если это имеет смысл.

Использование статического объекта в методе означает, что вы не знаете, когда он будет создан или удален. Вы также можете использовать глобальный статический объект в пространстве имен и вообще избегать getInstance() - это мало что добавляет.

Если вы используете темы, то у вас большие проблемы. В C++ практически невозможно создать пригодный для использования потокобезопасный синглтон из-за:

  1. постоянная блокировка в getInstance очень тяжелая - полное переключение контекста при каждом getInstance()
  2. Блокировка с двойной проверкой завершается неудачно из-за оптимизации компилятора и модели кеша / слабой памяти, очень сложна в реализации и невозможна для тестирования. Я не буду пытаться делать это в реальной системе, если вы не будете глубоко знать свою архитектуру и не хотите, чтобы она была переносимой

Их можно легко погуглить, но вот хорошая ссылка на слабую модель памяти: http://ridiculousfish.com/blog/archives/2007/02/17/barrier.

Одним из решений будет использование блокировки, но требуется, чтобы пользователи кэшировали указатель, полученный от getInctance(), и были готовы к тому, чтобы getInstance() был тяжелым.

Другим решением было бы позволить пользователям самим управлять безопасностью потоков.

Еще одним решением было бы использовать функцию с простой блокировкой и заменить ее другой функцией без блокировки и проверки после вызова new(). Это работает, но полная реализация сложна.

//! @file singleton.h
//!
//! @brief Variadic template to make a singleton out of an ordinary type.
//!
//! This template makes a singleton out of a type without a default
//! constructor.

#ifndef SINGLETON_H
#define SINGLETON_H

#include <stdexcept>

template <typename C, typename ...Args>
class singleton
{
private:
  singleton() = default;
  static C* m_instance;

public:
  singleton(const singleton&) = delete;
  singleton& operator=(const singleton&) = delete;
  singleton(singleton&&) = delete;
  singleton& operator=(singleton&&) = delete;

  ~singleton()
  {
    delete m_instance;
    m_instance = nullptr;
  }

  static C& create(Args...args)
  {
    if (m_instance != nullptr)
      {
    delete m_instance;
    m_instance = nullptr;
      }
    m_instance = new C(args...);
    return *m_instance;
  }

  static C& instance()
  {
    if (m_instance == nullptr)
      throw std::logic_error(
        "singleton<>::create(...) must precede singleton<>::instance()");
    return *m_instance;
  }
};

template <typename C, typename ...Args>
C* singleton<C, Args...>::m_instance = nullptr;

#endif // SINGLETON_H
Другие вопросы по тегам