Потокобезопасная фабрика синглтонов

Всем привет!

Нужно было такое дело для «фатального» избавления от глобальных переменных. Вот слегка наваял. Сперва лез в мьютексы и атомики, но народ подсказал новые фишки Стандарта С++ (в плане потокобезопасной инициализации статических структур). Вот оно, наваялось. Страждущий — да увидит :))

#include <iostream>
#include <memory>

// Прошу сей код распространять под лицензией MIT
// Majestio, Копировать Направо (R) 2016
// http://majestio.info

////////////////////////////////////////////////////////////////////////////////////////

template<typename T>
struct Holder {
  Holder() {
    std::cout << "Holder()::Holder()" << std::endl;
    if (!Self)  Self = new T();
    Self->Init();
  }
  ~Holder() {
    if (Self) {
      Self->Cleanup();
      delete Self;
      std::cout << "~Holder()::Holder()" << std::endl;
    }
  }
  T* Self = nullptr;
};

////////////////////////////////////////////////////////////////////////////////////////

template <typename T>
class Singleton {
  public:
    static T* Instance() {
      std::cout << "Singlton::Instance()" << std::endl;
      static Holder<T> Dummy;
      return Dummy.Self;
    }
  private:
    Singleton() = delete;
    Singleton(Singleton const&) = delete;
    Singleton& operator= (Singleton const&) = delete;
    Singleton(Singleton const&&) = delete;
    Singleton& operator= (Singleton const&&) = delete;
};

////////////////////////////////////////////////////////////////////////////////////////

class Base {
  public:
   virtual void Init() { std::cout << "Base::Init()" << std::endl; }
   virtual void Cleanup() { std::cout << "Base::Cleanup()" << std::endl; }
};

////////////////////////////////////////////////////////////////////////////////////////

class Config: public Base {
  public:
   int i = 0;
   void Init() override { std::cout << "Config::Init()" << std::endl; }
};

////////////////////////////////////////////////////////////////////////////////////////

class ConfigDerived: public Config {
  public:
    int j = 0;
    void Cleanup() override { std::cout << "ConfigDerived::Cleanup()" << std::endl; }
};

////////////////////////////////////////////////////////////////////////////////////////

int main() {
    std::cout << "===================================" << std::endl;
    auto R1 = Singleton<Config>::Instance();
    auto R2 = Singleton<ConfigDerived>::Instance();
    std::cout << "R1->i: " << R1->i << std::endl;
    R1->i = 7;
    std::cout << "R2->i: " << R2->i << std::endl;
    std::cout << "===================================" << std::endl;
    R2->i = 3;
    auto XX = Singleton<ConfigDerived>::Instance();
    std::cout << "XX->i: " << XX->i << std::endl;
    std::cout << "===================================" << std::endl;
    return 0;
}

если можно — на примере кода.

int main() {
    auto R1 = Singleton<Config>::Instance();
    Config c;
    c.Init();
}

и всё, у меня уже два объекта Config,
как видим, Ваш Singleton не дает гарантий того,
что объект будет один в программе.

Так то да. Но ведь на С++ можно и на нуль начать делить, и ничто этому не помешает. Вопрос, а надо ли.

Вопрос, а надо ли.

Вот именно. Если Ваш singleton не singleton, то зачем он нужен?
Сделать глобальный объект с защитой инициализации в многопоточной среде можно проще:

template<typename T>
T* instance()//возможно, стоит также удалить const, volatile, &, &&
{
   static T obj;
   return &obj;
}

Речь идет всеж не о синглтоне как таковом, а о фабрике синглтонов. У которых виртуализируется пост-инициализация и разрушение. В примере выше этого нет. А для меня это было принципиально важным. Вот за этим и было нужно.

Внимание! Это довольно старый топик, посты в него не попадут в новые, и их никто не увидит. Пишите пост, если хотите просто дополнить топик, а чтобы задать новый вопрос — начните новый.

Ответить

Вы можете использовать разметку markdown для оформления комментариев и постов. Используйте функцию предпросмотра для проверки корректности разметки.

Пожалуйста, оформляйте исходный код в соответствии с правилами разметки. Для того, чтобы вставить код в комментарий, скопируйте его в текстовое поле ниже, после чего выделите то, что скопировали и нажмите кнопку «код» в панели инструментов. Иначе ваш код может принять нечитаемый вид.

Либо производите оформление кода вручную, следующим образом:

``` #include <iostream> using namespace std; int main() { // ... } ```

Предпросмотр сообщения

Ваше сообщение пусто.