Может ли кто-нибудь предоставить мне образец Singleton в C ++? - PullRequest
36 голосов
/ 07 ноября 2008

Я пишу синглтон 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 ]

194 голосов
/ 07 ноября 2008

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

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

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

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

12 голосов
/ 07 ноября 2008

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

if(m_pA == 0) {
    static A static_instance;
    m_pA = &static_instance;
}
4 голосов
/ 07 ноября 2008

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

static A* A::GetInstance() {
    static A sin;
    return &sin;
}
2 голосов
/ 07 ноября 2008

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

1 голос
/ 07 ноября 2008

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

1 голос
/ 07 ноября 2008

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

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

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

1 голос
/ 07 ноября 2008

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

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

0 голосов
/ 10 января 2019
//! @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
0 голосов
/ 07 ноября 2008

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

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

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

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

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

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

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

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

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

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

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

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

0 голосов
/ 07 ноября 2008

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...