Scope einer statischen Klassenvariable / Singleton



  • Aquae schrieb:

    Ja.

    Also die Instanz ist wirklich global einzigartig und global zugänglich in allen Dateien, die die Kompilierungseinheit des Singletons linken?

    BTW: Singleton sollten keine Instanzen-Pointer liefern, sonst kann man auch ausserhalb des Singleton "deleten".

    und wie dann? Reference?



  • Genau, man könnte eine Referenz zurückgeben und gleichzeitig den &-operator verbieten.

    Eine interessante Diskussion über Singletons ist in Alexandrescu's Modern C++ Design zu finden.



  • Aquae schrieb:

    Genau, man könnte eine Referenz zurückgeben und gleichzeitig den &-operator verbieten.

    Eine interessante Diskussion über Singletons ist in Alexandrescu's Modern C++ Design zu finden.

    Du versuchst dich vor Machiavelli zu schützen und nicht vor murphy.
    das ist ein sinnloser ansatz, da ich sonst einfach ein
    #define private public
    mache und instance = -1 setze und alles fliegt in die luft 😉

    dennoch finde ich referenzen hier praktischer, da zeiger keinen mehrwert bringen...



  • Shade, du hast ja Recht, wenn man wirklich will, kann man immer "böse" Sachen machen.



  • Der Shade hat imemr fiese Ideen.

    Aber zur Singleton ich würde sie 1. ohne Zeiger und 2. ohne Zeiger machen. Eine Singleton existiert ja so oder so nur einmal.

    class Singleton
    {
    private:
        Singleton(){};
        Singleton(const Singleton&){};
        Singletoon& operator=(const Singleton&){};
    public:
        static Singleton& GetInstance()
        {
            static Singleton TheOne;
            return TheOne;
        };
        void Reset()
        {
            //diese Funktion kann man nutzen um die Klasse in den
            //Urspungszustand zurückzusetzen
        };
    };
    

    Das wäre mein Ansatz, keine Zeiger, kein Delete außerhalb der Klasse, und man kann sie auf das Konstruktionsniveau zurücksetzen. Einfach einmal am anfang von main aufrufen damit das Objekt da mit als erstes erstellt wird und fertig.



  • Xebov schrieb:

    ...

    Da es aber vorkommt, dass man mehrere Singletons hat und die in ner gewissen Reihenfolge zerstört werden sollen, nicht die beste Lösung...

    Ich persönlich mach es auch so, dass man mit dem &Operator den Pointer bekommen könnte und dann nen delete oder was weiß ich drauf loslassen könnte - aber naja - ab und an muss man eben auch mal vorraussetzen, dass der bibliotheksnutzer nich ganz verblödet ist...

    und mit nem privaten def-ctor wird es schwer, das singleton zu nutzen ^^

    assignment op und copy ctor implementiert man imho nicht (man definiert sie nur (private und so) )

    bb



  • Xebov schrieb:

    Der Shade hat imemr fiese Ideen.

    Aber zur Singleton ich würde sie 1. ohne Zeiger und 2. ohne Zeiger machen. Eine Singleton existiert ja so oder so nur einmal.

    --- gekürzt

    Das wäre mein Ansatz, keine Zeiger, kein Delete außerhalb der Klasse, und man kann sie auf das Konstruktionsniveau zurücksetzen. Einfach einmal am anfang von main aufrufen damit das Objekt da mit als erstes erstellt wird und fertig.

    Okay, aber wie sieht es mit dem deinierten delete aus? Wann verliert das statische TheOne seinen Gültigkeitsbereich? Ich muss nämlich hinter dem Objekt, das Unique sein soll, aufräumen.

    und mit nem privaten def-ctor wird es schwer, das singleton zu nutzen ^^

    Wieso? Man erzeugt doch keine Instanz, man macht doch Singleton::getInstance() ?



  • unskilled schrieb:

    Da es aber vorkommt, dass man mehrere Singletons hat und die in ner gewissen Reihenfolge zerstört werden sollen...

    Man hat nur ein Singleton eines Typs.



  • unskilled schrieb:

    Xebov schrieb:

    ...

    Da es aber vorkommt, dass man mehrere Singletons hat und die in ner gewissen Reihenfolge zerstört werden sollen, nicht die beste Lösung...

    PhilippM schrieb:

    Okay, aber wie sieht es mit dem deinierten delete aus? Wann verliert das statische TheOne seinen Gültigkeitsbereich? Ich muss nämlich hinter dem Objekt, das Unique sein soll, aufräumen.

    So für beide Quots zusammen folgendes:

    Diese Art und weise funktioniert sehr gut hier einmal ein Schema wonach ich das ganze immer aufbaue (inklusive der Funktionen die ich vorhin unterschlagen habe)

    class
    {
    private:
        Konstruktor;
        Copykonstruktor;
        Operator=;
    public:
        static GetInstance;
        Destructor;
        Init;
        ShutDown;
    };
    

    Zur Erklärung:

    • Konstruktor setzt Zeiger auf NULL und belegt Variablen vor, er macht aber nur das, dh kein new, kein Speicher Allocieren etc;
    • Copykonstruktor und Operator= gesichert um Kopien zu verhindern.
    • GetInstance enthält wie im Beispiel oben eine static Variable
    • Destruktor ruft ShutDown auf.
    • Init konstruiert alles was der Konstruktor nicht macht, er holt sich den Speicher und sorgt damit für die entgültige Konstruktion da der Konstruktor ja nur ein paar vorarbeiten leistet
    • ShutDown setzt die Instanz auf den Konstruktionsstand zurück, dh alle Objekte die mit new geholt wurden werden hier freigegeben am ende sieht die Instanz aus wie frisch konstruiert

    Das ganze Funktioneirt damit auch wenn abhängigkeiten vorhanden sind. Man muß in Main einmal GetInstance aufrufen und das Objekt dann wo man es braucht mit Init Initialisieren, danahc ist es voll funktionsfähig, wenn man es neu haben will ruft man ShutDown udn danach Init auf und hat ein neues Objekt. Man kann sich beim Beenden entscheiden ob man manuell mit ShutDown alles freigeben will oder ob man das ganze beim beenden des programms RAII und damit dem Destruktor überlässt. Man kann die Instanz wärend der Laufzeit nicht Löschen, dafür aber aufräumen.

    Anwendungsbeispiel:

    Ich habe 2 Singletons, eine Direct3D Verwaltung und einen Texturmanager, der Texturmanager lagert Texturen und lädt sie, der Direct3D Verwalter erstellt die Devices und gibt sie wieder frei. Erstellt werden muß das ganze in der Reihenfolge Direct3D->Texturmanager, aber freigegeben werden muß es Direct3D->Texturmanager, damit Scheidet RAII aus, da Direct3D vor dem Texturmanager da war, also wird einfach ShutDown aufgerufen, beide Klassen geben alel ihre Resourcen frei und deaktivieren sich damit, das beseitigen der Instanz übernimmt dann RAII.

    @PhilippM
    Ich habe alle meine Destruktoren imemr public, beugt ungewoltlen Problemen vor.

    c++fan 2009 schrieb:

    unskilled schrieb:

    Da es aber vorkommt, dass man mehrere Singletons hat und die in ner gewissen Reihenfolge zerstört werden sollen...

    Man hat nur ein Singleton eines Typs.

    Du verstehst ihn falsch, er meint es so das man mehrere Verschiedene Singletons hat die in bestimtmer Reihenfolge Konstruiert und wieder vernichtet werden müssen.



  • c++fan 2009 schrieb:

    unskilled schrieb:

    Da es aber vorkommt, dass man mehrere Singletons hat und die in ner gewissen Reihenfolge zerstört werden sollen...

    Man hat nur ein Singleton eines Typs.

    genau - und wenn du dann mal annimmst, dass ich doch zufällig weiß, über was ich rede, kannst du nochmal drüber nachdenken...

    Xebov schrieb:

    ...

    Du wiedersprichst dir selbst...
    du kannst nicht etwas auf NULL setzen, wenn du ohne zeiger arbeiten willst...
    die erklärung und der code aus deinem letzten post passt jedenfalls net zusammen...

    static Singleton& GetInstance()
        {
            static Singleton TheOne;
            return TheOne;
        };
    

    wie willst du TheOne jz im ctor auf NULL setzen?

    usw.

    ich würde auf so etwas kommen:
    (hätte jz aber noch immer die nachteile, dass man theone eben deleten kann, indem man über den &Operator geht (und wenn man den verboten hat, dann über BOOST_ADRESS_OF oder wie au immer das heißt):

    template <typename T>
    class Singleton
    {
    private:
    	static T* the_one;
    
    	static void Do_T();
    	static T* GetInstancePtr (); /*könnte man auch public machen - aber ich mag nirgendwo über pointer gehen, wenns au per ref geht...*/
    
    	Singleton& operator = (const Singleton &);
    	Singleton (const Singleton &);
    protected:
    	Singleton ()				{}
    public:
    	static void Delete();
    	~Singleton ()				{Delete();}
    
    	static T& GetInstance ()	{ return *GetInstancePtr(); }
    };
    
    template <typename T>
    T*	my::Singleton<T>::the_one = nullptr;
    
    template <typename T>
    T* my::Singleton<T>::GetInstancePtr ()
    {
    	if ( ! the_one )
    		Do_T ();
    	return the_one;
    }
    
    template <typename T>
    void my::Singleton<T>::Do_T()
    {
    	the_one = new T();
    }
    
    template <typename T>
    void my::Singleton<T>::Delete()
    {
    	delete the_one;
    	the_one = nullptr;
    }
    

    bb



  • unskilled schrieb:

    Xebov schrieb:

    ...

    Du wiedersprichst dir selbst...
    du kannst nicht etwas auf NULL setzen, wenn du ohne zeiger arbeiten willst...
    die erklärung und der code aus deinem letzten post passt jedenfalls net zusammen...

    static Singleton& GetInstance()
        {
            static Singleton TheOne;
            return TheOne;
        };
    

    wie willst du TheOne jz im ctor auf NULL setzen?

    Ich widerspreche mir nicht selbst du verstehst mich nur falsch.
    Wenn ich GetInstance das erste mal aufrufe wird ein Objekt vom Typ Singleton erstellt in diesem Fall TheOne und natürlich kann ich Variablen von TheOne im Konstruktor vorbelegen bzw Zeiger die evtl vorhanden sind auf NULL setzen denn der wird ja schließlich bei der Konstruktion aufgerufen. Es war nie die rede davon TheOne auf NULL zu setzen es ging dabei um Klassenmember, ich dachte das wäre soweit klar gewesen. Und die 2 Codeteile widersprechen sich auch nicht denn das 2. Beispiel ist lediglich eine erweietrung des ersten, ich habe dort lediglich 2 Funktionen angefügt die das Initialisieren und Deinitialisieren für einen regeln.

    EDIT:

    Hier ein etwas ausführlicheres Beispiel das wie das obrige auch genau zu meiner Beschreibung passt.

    class Singleton
    {
    private:
        //ein paar Variablen
        int m_iIrgendwas;
        bool *m_pbBools;
        myclass *m_pMyClass;
    
        Singleton()
        {
            m_iIrgendwas=10;
            m_pbBools=NULL;
            m_pMyClass=NULL;
        };
        Singleton(const Singleton&){};
        Singletoon& operator=(const Singleton&){};
    public:
        ~Singleton()
        {
            ShutDown();
        };
        static Singleton& GetInstance()
        {
            static Singleton TheOne;
            return TheOne;
        };
        void Init()
        {
            m_pbBools=new bool[10];
            m_pMyClass=new MyClass[5];
        };
        void ShutDown()
        {
            delete[] m_pbBools;
            m_pbBools=NULL;
            delete[] m_pMyClass;
            m_pMyClass=NULL;
            m_iIrgendwas=10;
        };
    };
    

    damit funktioniert dann folgendes:

    Singleton &TheOne=Singleton::GetInstance();//Objekt wird konstruiert
    TheOne.Init();//Initialisieren
    TheOne.ShutDown();//Deinitialisieren, also wieder wie frisch Konstruiert
    TheOne.Init();//und weis so schön war nochmal neu initialisiert
    

    Xebov schrieb:

    Zur Erklärung:

    • Konstruktor setzt Zeiger auf NULL und belegt Variablen vor, er macht aber nur das, dh kein new, kein Speicher Allocieren etc;
    • Copykonstruktor und Operator= gesichert um Kopien zu verhindern.
    • GetInstance enthält wie im Beispiel oben eine static Variable
    • Destruktor ruft ShutDown auf.
    • Init konstruiert alles was der Konstruktor nicht macht, er holt sich den Speicher und sorgt damit für die entgültige Konstruktion da der Konstruktor ja nur ein paar vorarbeiten leistet
    • ShutDown setzt die Instanz auf den Konstruktionsstand zurück, dh alle Objekte die mit new geholt wurden werden hier freigegeben am ende sieht die Instanz aus wie frisch konstruiert

    Sie tut genau das was da steht und nix anderes.


Anmelden zum Antworten