Может ли кто-нибудь предоставить мне образец 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
};
Обратите внимание, что вам также нужно сделать конструктор приватным. Также убедитесь, что вы переопределяете конструктор копирования по умолчанию и оператор присваивания, чтобы вы не могли сделать копию синглтона (иначе он не был бы синглтоном).
Также прочитайте:
- /questions/1083510/c-singleton-dizajn-shablona/1083522#1083522
- Синглтон: как его использовать
- C++ Singleton дизайн шаблона
Чтобы убедиться, что вы используете синглтон по правильным причинам.
Хотя технически не поточно-ориентирован в общем случае см.:
Каково время жизни статической переменной в функции 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
Эта реализация хороша, если вы можете ответить на следующие вопросы:
Вы знаете, когда объект будет создан (если вы используете статический объект вместо нового? У вас есть main()?)
есть ли у вас синглтон какие-либо зависимости, которые могут быть не готовы к моменту его создания? Если вы используете статический объект вместо нового, какие библиотеки были инициализированы к этому времени? Что ваш объект делает в конструкторе, который может их потребовать?
когда он будет удален?
Использование new() более безопасно, потому что вы контролируете, где и когда объект будет создан и удален. Но тогда вам нужно удалить его явно, и, вероятно, никто в системе не знает, когда это сделать. Вы можете использовать atexit() для этого, если это имеет смысл.
Использование статического объекта в методе означает, что вы не знаете, когда он будет создан или удален. Вы также можете использовать глобальный статический объект в пространстве имен и вообще избегать getInstance() - это мало что добавляет.
Если вы используете темы, то у вас большие проблемы. В C++ практически невозможно создать пригодный для использования потокобезопасный синглтон из-за:
- постоянная блокировка в getInstance очень тяжелая - полное переключение контекста при каждом getInstance()
- Блокировка с двойной проверкой завершается неудачно из-за оптимизации компилятора и модели кеша / слабой памяти, очень сложна в реализации и невозможна для тестирования. Я не буду пытаться делать это в реальной системе, если вы не будете глубоко знать свою архитектуру и не хотите, чтобы она была переносимой
Их можно легко погуглить, но вот хорошая ссылка на слабую модель памяти: 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