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

Я пишу singleton С++ следующим образом:

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. Ошибка Multithread + singleton error потеряла меня 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() {
}

Ответы

Ответ 1

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

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

Ответ 2

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

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

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

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
 };

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

Также читайте:

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

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

GCC имеет явный патч, чтобы компенсировать это:
http://gcc.gnu.org/ml/gcc-patches/2004-09/msg00265.html

Ответ 3

Синтаксис в С++ может быть записан следующим образом:

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

Ответ 4

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

Ответ 5

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

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

Ответ 6

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

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

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

Ответ 7

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

Ответ 8

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

Ответ 9

Эта реализация прекрасна, если вы можете ответить на эти вопросы:

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

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

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

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

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

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

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

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

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

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

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

Ответ 10

//! @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