Wzorce projektowe (8)

Autor: Damian Chodorek • Opublikowany: 21 lipca 2015 • Kategoria: kursy, wzorce projektowe

Singleton Fenix.

W poprzedniej części poznałeś wzorzec projektowy Singleton. Dowiedziałeś się także, że przy tak prostej jego implementacji może wystąpić problem martwych referencji. Wzorzec Singleton Fenix ma temu zapobiec.

Przykład

#include <cstdlib>
#include <iostream>

using namespace std;

class Singleton
{
public:
    static Singleton& instance()
    {
        if(!pInstance)
        {
          ///kontrola martwej referencji
          if(destroyed)
          {
            onDeadReference();
          }
          else
          {
            ///pierwsze wywołanie-inicjalizacja
            create();
          }
        }
        return *pInstance;
    }

    const char* method(const char* arr="...SingletonFenix...") { return arr; }

private:
    ///tworzenie instancji
    static void create()
    {
      //inicjalizujemy pInstance
      static Singleton theInstance;
      pInstance=&theInstance;
    }


    virtual ~Singleton()
    {
      pInstance=0;
      destroyed=true;
      std::cout<<"destruktor Singletona"<<std::endl;
    }

    ///wywołana w przypadku martwej referencji
    static void onDeadReference()
    {
       /// może być rzucenie wyjątku
       /// throw std::runtime_error("martwa referencja");
       ///albo zadbanie by obiekt stworzono ponownie:

       create();
       ///teraz pInstance wskazuje do pustego miejsca w pamięci
       ///w której utworzony był obiekt singletona
       ///wiec wypełniamy to puste miejsce:
       new(pInstance) Singleton;

       ///sprawiamy ze funkcja odpowiadająca za skasowanie
       ///utworzonego powyżej singletona, będzie wywołana
       ///przy wyjściu z programu:
       std::atexit(killSingleton);

       ///przestawiamy znacznik destroyed
      destroyed=false;
    }

    ///funkcja killSingleton istnieje tylko jeśli
    ///postanowiliśmy nie rzucać wyjątku ale stworzyć
    ///obiekt na nowo
    static void killSingleton()
    {
       pInstance->~Singleton();
    }


    ///dane klasy:
    static Singleton* pInstance;
    static bool destroyed;

    ///zablokowanie konstruktorów i operatora przypisania
    Singleton(const Singleton& obj){};
    Singleton(){std::cout<<"konstruktor Singleton"<<std::endl;};
    Singleton& operator=(const Singleton& obj){return instance();}

friend void rm();
};
///inicjalizacja zmiennych statycznych
Singleton* Singleton::pInstance=NULL;
bool Singleton::destroyed=false;



struct temp
{
  temp(){ std::cout<<"konstruktor temp"<<std::endl;}
  ~temp(){ std::cout<<Singleton::instance().method("destruktor temp")<<
        " uzywa Singletona"<<std::endl;}
};


void rm()
{
  Singleton::instance().~Singleton();
}


int main(int argc, const char*argv[])
{
 //Użycie
 temp t;

 {
   Singleton::instance();
   rm();///sztucznie wywołujemy destruktor singletona
 }
}

W kolejnej części poznasz wzorzec uogólnienie Singletona – Singleton Holder.

część 9

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany.