Иногда при разработке приложений возникает потребность в существовании некоего объекта, который должен быть доступен из многих, совершенно разных частей программы. Примером такого объекта может служить общий контейнер внутренней конфигурации программы. Кроме того, ключевой особенностью такого объекта является единственность его существования — к примеру, объект, работающий с определенным COM портом должен существовать в единственном экземпляре.
Таким образом, мы можем выделить две особенности объекта типа Singleton:
Специально выделяю вторую особенность, дабы подчеркнуть суть такого объекта. Первая особенность — просто «полезность», вытекающая из единственности. На практике, эти две особенности идут рядом, поэтому я не буду разделять их детали реализации.
Да, вопрос об обоснованности существования таких объектов и т.п. — это совсем другая тема и философия проектирования. Но мы сейчас не об этом.
Существует несколько подходов к реализации таких объектов. Я предлагаю один из них, которым пользуюсь уже на протяжении многих лет.
//-------------------------------------------------------------------------- // File: a_singleton.h // // Desc: An one-instance class //-------------------------------------------------------------------------- #ifndef A_SINGLETON_H #define A_SINGLETON_H #include <QtGlobal> // disable the warning regarding 'this' pointers being used in // base member initializer list. Singletons rely on this action #pragma warning(disable : 4355) //-------------------------------------------------------------------------- // Name: uSingleton // Desc: A singleton is a type of class of which only one instance may // exist. This is commonly used for management classes used to // control system-wide resources. //-------------------------------------------------------------------------- template <class T> class uSingleton { public: // the singleton must be constructed with a reference to the controlled object //---------------------------------------------------------------------- uSingleton(T& rObject) { Q_ASSERT_X(!s_pInstance, "constructor", "Only one instance of this class is permitted."); s_pInstance = &rObject; } // the singleton accessor //---------------------------------------------------------------------- ~uSingleton() { Q_ASSERT_X(s_pInstance, "destructor", "The singleton instance is invalid."); s_pInstance = 0; } // the singleton accessor //---------------------------------------------------------------------- static T& instance() { Q_ASSERT_X(s_pInstance, "instancing", "The singleton has not yet been created."); return (*s_pInstance); } private: // Data... //---------------------------------------------------------------------- static T* s_pInstance; // Nonexistant Functions... //---------------------------------------------------------------------- uSingleton(const uSingleton& Src); uSingleton& operator=(const uSingleton& Src); }; template <class T> T* uSingleton<T>::s_pInstance = 0; #endif // A_SINGLETON_H
Объявление класса:
//-------------------------------------------------------------------------- // File: usingletonuserclass.h //-------------------------------------------------------------------------- #ifndef USINGLETONUSERCLASS_H #define USINGLETONUSERCLASS_H #include <QObject> #include "a_singleton.h" // Defines for geting instance class MyClass; #define MY_CLASS MyClass::instance() //-------------------------------------------------------------------------- // Name: MyClass //-------------------------------------------------------------------------- class MyClass : public QObject, public uSingleton<MyClass> { Q_OBJECT public: explicit MyClass(QObject* parent = 0); // Test function to show work with singleton void testFunc() {}; }; #endif // USINGLETONUSERCLASS_H
Не забудьте инициализировать синглетон в конструкторе:
//-------------------------------------------------------------------------- // MyClass singleton constructor //-------------------------------------------------------------------------- MyClass::MyClass(QObject *parent) : QObject(parent), uSingleton<MyClass>(*this) { /* ... */ }
Теперь доступ к объекту класса производится простым и понятным путем:
// ... MY_CLASS.testFunc(); // ...