Statische Klassen



  • Reth schrieb:

    ...
    Aber ich sehe da keinen so großen Unterschied, da eine Klasse an sich doch auch schon einen Namespace liefert, wenn sie nur aus statischen Methoden besteht!?

    Ist das gängige Praxis in C++, dass wenn man nur statische Dinge hat man diese in nen Namespace packt anstatt in eine Klasse?...

    Der Vorteil ist, dass man in einem namespace weniger "Verwirrungspotential" hat. Man kommt nicht in Versuchung, eine Instanz anzulegen oder abzuleiten oder Anderes zu tun, wofür eine "reine Fuktionssammlung" nicht ausgelegt ist.

    Reth schrieb:

    ...
    Wie macht man das dann mit Zugriffschutz? In ner Klasse mit nur statischen Methoden kann ich immerhin noch private Member anlegen, die nur über diese Methoden zugreifbar sind!...

    Eigentlich ist der "Zugriffschutz" entworfen worden zur Kapselung von Daten ... und solche hast Du in diesem Fall gar nicht.
    Wenn Du zwischen "externen" und "Hilfsfunktionen" unterscheiden (oder eine konkrete Implementierung verstecken) willst, bietet sich da die "klassische Modularisierung" viel eher an:

    // meineFunktionen.h
    namespace meineFunktionen {
       int f(int);
       int g(int);
    }
    
    // meineFunktionen.cpp
    #include "meineFunktionen.h"
    
    namespace { // anonymer namespace
       int Hilfsfunktion1(int a) { return a+3; }
       int Hilfsfunktion2(int a) { return a+5; }
    }
    // Implementation
    namespace meineFunktionen {
       int f(int x) { return Hilfsfunktion1(x*2); }
       int g(int x) { return Hilfsfunktion2(x*3); }
    }
    

    Da kannst Du sogar Hilfsfunktionen zufügen/Namen ändern/..., ohne dass ein Benutzer von f() und g() neu compilieren müsste. Und für statische Variablen geht das genauso.

    Reth schrieb:

    ...
    Aber wie initialisiere ich so ein privates Member dann (z.B. nen STL-Vector)?
    Ciao

    Ein privates Mamber hat in einer Klasse mit nur statischen Funktionen keinen Sinn, weil Du gar keinen Zugriff darauf hast (wenn Du nur statische Funktionen hast).

    Gruß,

    Simon2.



  • Simon2 schrieb:

    Reth schrieb:

    ...
    Der Vorteil ist, dass man in einem namespace weniger "Verwirrungspotential" hat. Man kommt nicht in Versuchung, eine Instanz anzulegen oder abzuleiten oder Anderes zu tun, wofür eine "reine Fuktionssammlung" nicht ausgelegt ist.

    Dazu habe ich den Konstruktor private gemacht.

    Eigentlich ist der "Zugriffschutz" entworfen worden zur Kapselung von Daten ... und solche hast Du in diesem Fall gar nicht.
    Wenn Du zwischen "externen" und "Hilfsfunktionen" unterscheiden (oder eine konkrete Implementierung verstecken) willst, bietet sich da die "klassische Modularisierung" viel eher an:

    // meineFunktionen.h
    namespace meineFunktionen {
       int f(int);
       int g(int);
    }
    
    // meineFunktionen.cpp
    #include "meineFunktionen.h"
    
    namespace { // anonymer namespace
       int Hilfsfunktion1(int a) { return a+3; }
       int Hilfsfunktion2(int a) { return a+5; }
    }
    // Implementation
    namespace meineFunktionen {
       int f(int x) { return Hilfsfunktion1(x*2); }
       int g(int x) { return Hilfsfunktion2(x*3); }
    }
    

    Da kannst Du sogar Hilfsfunktionen zufügen/Namen ändern/..., ohne dass ein Benutzer von f() und g() neu compilieren müsste. Und für statische Variablen geht das genauso.

    Zugriffsschutz meinte ich in dem Sinne, dass ich eine private Variable in der Klasse brauche, auf die nur die Klasse Zugriff haben soll. Geht das auch mit Namespaces?

    Zudem fehlt mir bei einer Funktionssammlung irgendwie die Objektorientiertheit.

    Reth schrieb:

    ...
    Aber wie initialisiere ich so ein privates Member dann (z.B. nen STL-Vector)?
    Ciao

    Ein privates Mamber hat in einer Klasse mit nur statischen Funktionen keinen Sinn, weil Du gar keinen Zugriff darauf hast (wenn Du nur statische Funktionen hast).

    Ich denke doch, wenn denn das Member ist private und statisch.
    Mir geht es darum, dass diese Klasse alleinig für die Erzeugung, Entsorgung und Verwaltung bestimmter Objekte zuständig ist.

    Dazu würde auch ein Singleton/Factory-Ansatz funktionieren, dann hätte ich aber das Problem, dass überall dort, wo ich diese(s) Singleton/Factory benötige irgendwie eine Referenz oder einen Zeiger darauf hinbekommen muss, d.h. überall einen weiteren Übergabeparameter.

    Mit ner statischen Klasse kann ich das umgehen, die kann ich nach dem include von überall aus zugreifen!

    Vllt. kann ich später noch den Code posten.

    Ciao



  • warum einen weiteren übergabe parameter?

    //header.h
    
    class Singleton
    {
    //...
    public: Singleton& get_instance () { /* impl siehe oben */ }
    };
    
    //irgendeine_unit.cc
    void foo () //notiere: kein singleton-argument nötig
    {
      Singleton &factory = Singleton::get_instance();
      factory.create_new_blubb();
    }
    

    ach, und übrigens: nur wenn man alles in klassen packt, ist das noch lange nicht "objekt" orientiert.



  • queer_boy schrieb:

    warum einen weiteren übergabe parameter?

    //header.h
    
    class Singleton
    {
    //...
    public: Singleton& get_instance () { /* impl siehe oben */ }
    };
    
    //irgendeine_unit.cc
    void foo () //notiere: kein singleton-argument nötig
    {
      Singleton &factory = Singleton::get_instance();
      factory.create_new_blubb();
    }
    

    Gute Idee das Singleton so einzurichten, aber hat der statische Ansatz nicht noch ein paar Compile-, Memory-, usw. Vorteile?
    Und kann man nicht-statische Methoden einer Klasse (getInstance() in diesem Falle), denn so wie in foo() angegeben ohne referenzierendes Objekt aufrufen?
    Das ist mir neu!

    /* impl siehe oben */

    Hab ich was übersehen?

    ach, und übrigens: nur wenn man alles in klassen packt, ist das noch lange nicht "objekt" orientiert.

    Mir ist schon klar, dass es noch lang kein OO ist, wenn man alles in Klassen packt!

    Aber es ist keins, wenn man sich Hilfsfunktionen in Namespaces legt (naja, kommt natürlich immer auf die Betrachtungsweise an; z.B. wenn man OO nicht mit Klassen umsetzt, sondern mit anderen Ansätzen, so dass ein Namespace zu einer Einheit wird, die ein Objekt der realen Welt widerspiegelt, ums mal platt auf Vorlesungsdeutsch zu sagen - bin aber noch zu sehr C++ Anfänger, um das Beurteilen zu können)!

    Ciao



  • Reth schrieb:

    Gute Idee das Singleton so einzurichten, aber hat der statische Ansatz nicht noch ein paar Compile-, Memory-, usw. Vorteile?

    Nein. Es wird der gleiche Speicherplatz verwendet, aber dessen Initialisierung ist zu einem definierten Zeitpunkt. Die ganzen instance()-Aufrufe werden wegoptimiert.

    Reth schrieb:

    Und kann man nicht-statische Methoden einer Klasse (getInstance() in diesem Falle), denn so wie in foo() angegeben ohne referenzierendes Objekt aufrufen?
    Das ist mir neu!

    Mir auch ;). Ist ein Schreibfehler. Bei meinem Code ist das static da und es gehört auch dahin.

    Reth schrieb:

    /* impl siehe oben */

    Hab ich was übersehen?

    Du musst nirgendwo in deinem Programm irgendwelche Referenzen oder Zeiger auf das Objekt durch die Gegend reichen. Jede Funktion kommt ohne Umwege direkt über die statische instance-Methode an das (einzige!) Objekt der Klasse. Damit du nicht so viel schreiben musst, kannst du aber auch am Anfang einer Funktion, in der das Objekt häufig benötigt wird eine Referenz anlegen (wird auch wegoptimiert).

    Reth schrieb:

    ach, und übrigens: nur wenn man alles in klassen packt, ist das noch lange nicht "objekt" orientiert.

    Aber es ist keins, wenn man sich Hilfsfunktionen in Namespaces legt (naja, kommt natürlich immer auf die Betrachtungsweise an; z.B. wenn man OO nicht mit Klassen umsetzt, sondern mit anderen Ansätzen, so dass ein Namespace zu einer Einheit wird, die ein Objekt der realen Welt widerspiegelt, ums mal platt auf Vorlesungsdeutsch zu sagen - bin aber noch zu sehr C++ Anfänger, um das Beurteilen zu können)!

    [/quote]IMHO ist der einzige, akzeptable objektorientierte Ansatz das Singleton-Pattern. Der namespace-Kram macht die Kapselung kaputt, die static-Methoden haben die oben beschriebenen Probleme.



  • .filmor schrieb:

    IMHO ist der einzige, akzeptable objektorientierte Ansatz das Singleton-Pattern. Der namespace-Kram macht die Kapselung kaputt, die static-Methoden haben die oben beschriebenen Probleme.

    Ja, der Singleton-Ansatz gefällt mir auch am besten.
    Danke nochmals dafür!

    Hm, hab den Thread nochmal durchgelesen, welche beschriebenen Probleme der statischen Methoden meinst Du genau?

    Ciao



  • Reth schrieb:

    Ist das gängige Praxis in C++, dass wenn man nur statische Dinge hat man diese in nen Namespace packt anstatt in eine Klasse?

    Man sollte immer das nehmen was passt. Eine Klasse ist eine Klasse und keine "Sammlung von freien Funktionen". Freie Funktionen sollte man also als eben solche Implementieren.

    Sobald etwas allerdings Daten ("State") hat ist fast immer eine Klasse die richtige Wahl, nur sollte diese dann auch mehrfach instanzierbar sein.
    Die Erklärung warum das fast immer eine gute Idee ist, und eben ein Singleton eine schlechte Idee ist würde fürchte ich etwas lange, also glaubs mir einfach 🙂

    Die beste Lösung ist meistens explizit eine Referenz (oder Pointer oder shared_ptr) auf die zu verwendende Instanz rumzureichen. Das ist zwar etwas mehr Tippaufwand, aber sicherlich die saubrerer Lösung.

    p.S.: die Formulierung "gängige Praxis" habe ich absichtlich nicht verwendet, da in C++ leider viele "unsaubere" Praktiken "gängig" sind.



  • hustbaer schrieb:

    Sobald etwas allerdings Daten ("State") hat ist fast immer eine Klasse die richtige Wahl, nur sollte diese dann auch mehrfach instanzierbar sein.
    Die Erklärung warum das fast immer eine gute Idee ist, und eben ein Singleton eine schlechte Idee ist würde fürchte ich etwas lange, also glaubs mir einfach 🙂

    Kannst Du denn einen kurzen Tipp in diese Richtung bringen (wenn das ansonsten zu lang wird)?

    In meinem Fall ist die Klasse, die als Singleton arbeiten soll ja eine Factory, in der Objekte erzeugt, verwaltet und am Ende aufgeräumt werden sollen.
    Es handelt sich dabei um alle grafischen Objekte einer Applikation.
    Aus dem Grund denke ich, dass mehrere Instanzen dieser Factory pro Applikation unnötig sind, da alle grafischen Objekte in einer Instanz (pro Applikation) verwaltet werden sollten.

    Natürlich könnte man auch mehrere solcher Instanzen innerhalb einer Applikation erzeugen und verwenden, allerdings sehe ich da keinen Sinn drin.

    Was wären denn die Argumente für mehrere Instanzen in meinem Fall?
    (evtl. grafische Objekte pro Fenster innerhalb der Applikation? Aber z.B. eine Knopfgrafik sollte ja in versch. Fenstern verwendbar sein, ohne dass sie mehrfach im Speicher vorgehalten wird!)

    Ciao



  • .filmor schrieb:

    Besser fährst du meistens mit einem Meyers-Singleton:

    class Factory
    {
    public:
        static Factory& instance ()
        {
            static Factory* _instance = 0;
            if (!_instance) _instance = new Factory;
            return &_instance;
        }
    
    private:
        Factory ();
        // kopieren noch verhindern
    };
    

    Eine Frage dazu noch!
    Nach meinem Verständnis ist der scope von _instance doch nur die Methode instance(), oder?
    D.h. nach dem Verlassen der Methode ist dieser Zeiger undefiniert, nicht?

    Muss die Zeigervariable für die Singleton-Instanz denn kein statisches privates Klassenmember sein?

    Ciao



  • hmm das ist sicherlich nicht die normale implementierung des Meyer'schen Singletons.



  • Reth schrieb:

    Eine Frage dazu noch!
    Nach meinem Verständnis ist der scope von _instance doch nur die Methode instance(), oder?
    D.h. nach dem Verlassen der Methode ist dieser Zeiger undefiniert, nicht?

    Muss die Zeigervariable für die Singleton-Instanz denn kein statisches privates Klassenmember sein?

    Nein. Dafür ist das static innerhalb der Methode. Das hat den Effekt, dass diese Variable der Methode selbst "gehört". Der Konstruktor der Variable wird beim ersten Methodenaufruf aufgerufen und der Zeiger deshalb mit initialisiert.

    (D)Evil schrieb:

    hmm das ist sicherlich nicht die normale implementierung des Meyer'schen Singletons.

    Was ist denn die Normale? Ich dachte, das wäre sie. Man kann natürlich noch statt eines Zeigers einfach eine Variable verwenden, wenns einem Spaß macht. Aber die Essenz ist doch einfach nur, dass die Instanz in der Funktion liegt und erst erzeugt wird, wenn sie benötigt wird.



  • Reth schrieb:

    ...
    Ich denke doch, wenn denn das Member ist private und statisch....

    Das bedeutet doch wiederum nichts Anderes als was ich oben beschrieb: Eine "globale Variable für Deinen internen Gebrauch".... und da halte ich den "klassischen Ansatz" für besser, weil er stärker kapselt (also stärker als "private"):

    //meineFunktionen.cpp
    namespace { // Anonymer namespace
       int glob1 = 3; // geht genauso wie meine "Hilfsfunktionen" oben
    }
    
    namespace meineFunktionen {
       int f(int x) { return x * glob1++; }
    }
    

    Jetzt kann nicht nur niemand außerhalb Deines Moduls darauf zugreifen, sondern er sieht sie nicht mal ! Also: Keine Probleme mit Mehrdeutigkeiten, nur Linkabhängigkeit (wenn Du glob1 mal verändern willst), ....
    Ich sehe immer noch keinen Vorteil in einem privatem statischen Element in einer Klasse ... und auch nicht von einer "statischen Klasse".

    Reth schrieb:

    ...Mit ner statischen Klasse kann ich das umgehen, die kann ich nach dem include von überall aus zugreifen!...

    Aber auf Dein private Member soll doch keiner drauf zugreifen !! (sonst wäre es wohl nicht private) ... damit sehe ich nicht ein, warum es alle "sehen" sollten.

    Gruß,

    Simon2.



  • .filmor schrieb:

    (D)Evil schrieb:

    hmm das ist sicherlich nicht die normale implementierung des Meyer'schen Singletons.

    Was ist denn die Normale? Ich dachte, das wäre sie. Man kann natürlich noch statt eines Zeigers einfach eine Variable verwenden, wenns einem Spaß macht. Aber die Essenz ist doch einfach nur, dass die Instanz in der Funktion liegt und erst erzeugt wird, wenn sie benötigt wird.

    Könnte man. Spart Schreibarbeit und hat technisch fast denselben Effekt.
    Ich meine aber auch gelesen zu haben, dass das klassische Meyers-Singleton sich gerade dadurch auszeichnet, dass es die lazy-initialization von funktionslokalen statischen Variablen ausnutzt.

    static Factory& instance ()
        {
            static Factory _instance;
            return _instance;
        }
    


  • Simon2 schrieb:

    Reth schrieb:

    ...Mit ner statischen Klasse kann ich das umgehen, die kann ich nach dem include von überall aus zugreifen!...

    Aber auf Dein private Member soll doch keiner drauf zugreifen !! (sonst wäre es wohl nicht private) ... damit sehe ich nicht ein, warum es alle "sehen" sollten.

    Entweder ich verstehs gerad nicht, oder wir reden aneinander vorbei?

    Auf das private Member soll natürlich keiner zugreifen (daher private), nur die Methoden der Klasse, in der dieses Member vorhanden ist (also createAddFrame() und getFrames() aus meinem Code)!

    Und wieso sieht dieses Member jeder? Es ist doch private (oder hast Du das vllt. so verstanden, dass eine Referenz/ein Zeiger auf dieses Member aus der Klasse herausgegeben werden soll? Das passiert natürlich nicht [s. geposteten Code]!)!

    Im Prinzip hast Du recht, es handelt sich hierbei um eine Funktionssammlung für eine spezifische Aufgabe (wie in einem meiner letzten Postings beschrieben), die aber für ihre Erledigung einen Container (daher das private Member) benötigt.
    Das private Member musste ich deswegen statisch machen, weil die Methoden der Klasse, welche es benutzen auch statisch sind!

    LordJaxom schrieb:

    Könnte man. Spart Schreibarbeit und hat technisch fast denselben Effekt.
    Ich meine aber auch gelesen zu haben, dass das klassische Meyers-Singleton sich gerade dadurch auszeichnet, dass es die lazy-initialization von funktionslokalen statischen Variablen ausnutzt.

    static Factory& instance ()
        {
            static Factory _instance;
            return _instance;
        }
    

    Ich verstehe aber immer noch nicht, wieso die statische Methodenvariable ihren Inhalt nach Verlassen der Methode behält!?

    Ciao



  • Reth schrieb:

    Ich verstehe aber immer noch nicht, wieso die statische Methodenvariable ihren Inhalt nach Verlassen der Methode behält!?

    Weil das der Sinn und Zweck von statischen Funktionsvariablen ist. Sie behalten ihren Wert über Aufrufe hinweg.



  • Wird dabei der Scope der Variablen total vernachlässigt?
    Der wirkt sich ja dann nur noch auf die Zugreifbarkeit aus, aber nicht mehr auf die Gültigkeit, oder?

    Ciao



  • Genau. Sichtbarkeit ist nur innherhalb der Funktion gegeben, Gültigkeit jedoch während annähernd der gesamten Programmlaufzeit.



  • Hi,

    Reth schrieb:

    Simon2 schrieb:

    Reth schrieb:

    ...Mit ner statischen Klasse kann ich das umgehen, die kann ich nach dem include von überall aus zugreifen!...

    Aber auf Dein private Member soll doch keiner drauf zugreifen !! (sonst wäre es wohl nicht private) ... damit sehe ich nicht ein, warum es alle "sehen" sollten.

    Entweder ich verstehs gerad nicht, oder wir reden aneinander vorbei?

    Auf das private Member soll natürlich keiner zugreifen (daher private), nur die Methoden der Klasse, in der dieses Member vorhanden ist (also createAddFrame() und getFrames() aus meinem Code)!

    Und wieso sieht dieses Member jeder? ...

    Ganz einfach: Weil es in Deinem Header steht, den jeder, der Deine Funktionen nutzen möchte, einbinden muss !

    Stell Dir einfach vor, dass sich zwischen dem 2. und 3. Release herausstellt, dass Dein ursprünglich gewählter Container verändert werden muss (z.B. bekommt die map einen anderen key oder Du schwenkst auf eine multimap oder ...): Dann muss jeder Nutzer Deiner Lib

    • einen neuen Header einbringen und
    • seinen ganzen Code neu compilieren.

    Wenn Du die globale Variable in Deiner cpp einführst, compilerst Du einmal neu, bindest Deine lib neu und drückst sie Deinen Kunden in die Hand - fertig. Sie können sogar mit der alten witerarbeiten, wenn sie die neue Version nicht unbedingt brauchen (alter Header ist zu neuer Lib kompatibel).

    Prinzipiell sollte man sich bemühen, die Kopplung so gering wie möglich zu halten.

    Reth schrieb:

    ...es handelt sich hierbei um eine Funktionssammlung für eine spezifische Aufgabe (wie in einem meiner letzten Postings beschrieben), die aber für ihre Erledigung einen Container (daher das private Member) benötigt....

    "einen Container benötigen": Ja
    "ein privates Member benötigen": Nein 😃

    Dieser Container muss gemeinsam sein (deswegen global und statisch) - mehr nicht. 😃

    Gruß,

    Simon2.



  • Simon2 schrieb:

    ...

    //meineFunktionen.cpp
    namespace meineGlobalen {
       int glob1 = 3; // geht genauso wie meine "Hilfsfunktionen" oben
    }
    
    namespace meineFunktionen {
       int f(int x) { return x * glob1++; }
    }
    

    Jetzt kann nicht nur niemand außerhalb Deines Moduls darauf zugreifen, sondern er sieht sie nicht mal ! Also: Keine Probleme mit Mehrdeutigkeiten, nur Linkabhängigkeit (wenn Du glob1 mal verändern willst), ....
    ...

    Du solltest hier wirklich einen nameless namespace (aka. anonymous namespace) verwenden. Einen nicht dokumentierten namespace "meineGlobalen" zu verwenden in der Hoffnung dass niemand anders einen namespace so nennen wird... jack pfui, wer macht denn sowas.

    Und nur damit klar ist was ich meine: es kann *jeder* sofort von aussen darauf zugreifen, und zwar so:

    namespace meineGlobalen {
       extern int glob1;
    }
    
    void foo()
    {
        meineGlobalen::glob1 = 42;
    }
    


  • hustbaer schrieb:

    ...
    Du solltest hier wirklich einen nameless namespace (aka. anonymous namespace) verwenden. ...

    Stimmt ! Hatte mich "vertippt" ... in meinem vorherigen Beispiel ("Hilfsfunktionen") war es noch drin.

    Danke für den Hinweis,

    Simon2.


Anmelden zum Antworten