Singleton kuerzer?



  • hustbaer schrieb:

    Und damit ist (Korrektur: wäre) auch bloss das Thema Initialisierung erledigt. Das Cleanup-Problem löst sich dadurch nicht.

    Bitte nicht hauen 😉 aber ich nehme das nicht allzu ernst. Klar, wenn man sich theoretisch mit Singletons beschäftigt oder eine allgemeine Klasse schreiben möchte, sollte man sich schon mit Initialisierung und Cleanup auseinandersetzen. Sei es, um Kunden der Klasse über die Einsatz-Bedingungen aufzuklären.

    Andererseits aber funktionieren konkrete Singletons meiner Erfahrung nach sehr gut. Es gibt oft gar keinen Grund, sich um die (theoretischen) Schwierigkeiten zu kümmern.

    Und außerdem: Was wäre die Alternative? Offene globale Variablen sicher nicht. Das Herumreichen von Objekten meist auch nicht. Insgesamt meine ich, dass man die Gefahren des Patterns schon kennen sollte - diese aber andererseits bei konkreten Anwendungsfällen gar nicht ins Gewicht fallen. Und bevor man sich mit den Alternativen einen abbricht, nimmt man halt ein Singleton.

    Stefan.



  • Warum ist ein Singleton globalen Funktionen + Namespace überlegen? Warum nicht einfach:

    namespace RandomNumberGenerator
    {
        unsigned get_counter();
        double uniform_01();
    }
    


  • Ben04 schrieb:

    Warum ist ein Singleton globalen Funktionen + Namespace überlegen? Warum nicht einfach:

    namespace RandomNumberGenerator
    {
        unsigned get_counter();
        double uniform_01();
    }
    

    Weil hinter einem Singleton eine Klasse steckt, die Member kapselt. Somit muss nicht irgendwo ein globales Objekt runliegen, wenn du mit mehreren Methoden darauf zugreifen willst.
    Z.B....



  • Ben04 schrieb:

    Warum ist ein Singleton globalen Funktionen + Namespace überlegen? Warum nicht einfach:

    namespace RandomNumberGenerator
    {
        unsigned get_counter();
        double uniform_01();
    }
    

    Lokale Zufallszahlengeneratoren können Vorteile haben. Deswegen mag man "class RandomNumberGenerator". Meistens braucht man aber dann doch nur einen globalen und den macht man zum Singleton, damit das Kind keinen so häßlichen Namen hat.



  • Joladrio schrieb:

    Ben04 schrieb:

    Warum ist ein Singleton globalen Funktionen + Namespace überlegen? Warum nicht einfach:

    namespace RandomNumberGenerator
    {
        unsigned get_counter();
        double uniform_01();
    }
    

    Weil hinter einem Singleton eine Klasse steckt, die Member kapselt. Somit muss nicht irgendwo ein globales Objekt runliegen, wenn du mit mehreren Methoden darauf zugreifen willst.
    Z.B....

    namespace RandomNumberGenerator
    {
        unsigned get_counter();
        double uniform_01();
    }
    // ...
    namespace RandomNumberGenerator
    {
        static int my_data = 0;
        unsigned get_counter(){
             ...
        }
    }
    

    Welche globale Variable?

    volkard schrieb:

    Ben04 schrieb:

    Warum ist ein Singleton globalen Funktionen + Namespace überlegen? Warum nicht einfach:

    namespace RandomNumberGenerator
    {
        unsigned get_counter();
        double uniform_01();
    }
    

    Lokale Zufallszahlengeneratoren können Vorteile haben. Deswegen mag man "class RandomNumberGenerator". Meistens braucht man aber dann doch nur einen globalen und den macht man zum Singleton, damit das Kind keinen so häßlichen Namen hat.

    Du willst also kein Singleton sondern eine globale Instanz deiner Klasse. Lokal instantiieren kann man das Singleton aus dem ersten Post jedenfalls nicht.



  • Ben04 schrieb:

    Du willst also kein Singleton sondern eine globale Instanz deiner Klasse.

    Im Prinzip schon. Wobei das späte Initialisieren zum Beispiel beim Mersenne Prime Twin Generator auch ganz lecker ist. Ich will aber keinen Zwang, daß es nur eine Instanz geben kann.

    Ben04 schrieb:

    Lokal instantiieren kann man das Singleton aus dem ersten Post jedenfalls nicht.

    Jetzt in ich ein trauriger Programmierbär.



  • Ben04 schrieb:

    namespace RandomNumberGenerator
    {
        unsigned get_counter();
        double uniform_01();
    }
    // ...
    namespace RandomNumberGenerator
    {
        static int my_data = 0;
        unsigned get_counter(){
             ...
        }
    }
    

    Welche globale Variable?

    OK, global ist sie nicht, aber eben nicht gekapselt.
    Ich kann von außen RandomNumberGenerator::my_data verändern, ohne dass die Methoden etwas davon mibekommen, und das kann ziemlich Mist erzeugen. Im Falle eines Zufallszahlengenerators z.B. eine nicht reproduzierbare Änderung in der Zahlenfolge.
    Nicht nett, dass du das eigentliche Argument, nämlich die Kapselung durch die Klasse, einfach unterschlägst und auf einem falsch verwendeten Begriff rumreitest. 😞



  • Joladrio schrieb:

    OK, global ist sie nicht, aber eben nicht gekapselt.

    Doch. Sogar besser, da die außen Welt noch nicht mal von ihrer Existenz zu wissen braucht.

    Joladrio schrieb:

    Ich kann von außen RandomNumberGenerator::my_data verändern

    Nein, das geht nicht (außer du packst mehr in die cpp Datei als du solltest). Noch nicht mal mit wildem Casten kommst du an die Variable ran.



  • DStefan schrieb:

    Andererseits aber funktionieren konkrete Singletons meiner Erfahrung nach sehr gut. Es gibt oft gar keinen Grund, sich um die (theoretischen) Schwierigkeiten zu kümmern.

    Mach mal Logging mit einem Singleton-Logger.

    Und dann mach bitte Log-Ausgaben im Destruktor von anderen Singleton-Klassen.
    Oder im Destruktor von anderen Klassen, die irgendwo auch mal auf File-Scope instanziert werden.

    p.S.:
    Die "beste" Möglichkeit sich um die Zerstörungs-Problematik zu kümmern, ist IMO oft, die Singletons einfach garnicht zu zerstören. Einfach mit "new" anfordern, und nie ein "delete" drauf machen.

    Bei Dingen wie einem Log-Writer kann das natürlich zu anderen Problemen führen. z.B. wenn man buffered Writes ins Logfile macht. Dann können schnell mal die letzten paar Zeilen fehlen. Kann man aber z.B. dadurch vermeiden, dass man (über ein Objekt einer Hilfsklasse) während der Statischen Deinitialisierungsphase das Buffering ausschaltet.



  • Was Zufallszahlen-Generatoren angeht: niemals global/Singleton.
    Warum? Weil man sonst ein Problem mit Threads bekommt. Und Zugriffe auf einen RNG zu synchronisieren ist ... nunja ... ziemlich grober Overhead.



  • hustbaer schrieb:

    DStefan schrieb:

    Andererseits aber funktionieren konkrete Singletons meiner Erfahrung nach sehr gut. Es gibt oft gar keinen Grund, sich um die (theoretischen) Schwierigkeiten zu kümmern.

    Mach mal Logging mit einem Singleton-Logger.

    Und dann mach bitte Log-Ausgaben im Destruktor von anderen Singleton-Klassen.
    Oder im Destruktor von anderen Klassen, die irgendwo auch mal auf File-Scope instanziert werden.

    Ja klar, ein Logger ist natürlich das Killer-Beispiel. Da rennst du bei mir offene Türen ein 😉

    Ich wollte auch nicht sagen, dass das Konzept Singleton trivial sei, sondern bloß, dass konkrete Singletons oft ganz ohne Fisematenten bestens funktionieren.

    Sozusagen der pragmatische Ansatz.

    hustbaer schrieb:

    p.S.:
    Die "beste" Möglichkeit sich um die Zerstörungs-Problematik zu kümmern, ist IMO oft, die Singletons einfach garnicht zu zerstören. Einfach mit "new" anfordern, und nie ein "delete" drauf machen.

    Genauso mache ich es im Zweifelsfall auch. Und verlasse mich darauf, dass mein OS den Speicher am Programm-Ende ohnehin frei gibt.

    Stefan.



  • Naja... Logging ist IMO nicht "das Killer Beispiel".

    Jedes Singleton, welches
    a) in Destruktoren anderer Klassen verwendet werden könnte und
    b) selbst irgendwelche "Resourcen" ausser Speicher (File-Handles, DB-Connections etc.) hat
    ist IMO problematisch.

    D.h. eigentlich fast alles, was man nicht einfach mit 1-2 globalen Variablen (versteckt im namenlosen Namespace im cpp File) auch machen könnte.



  • hustbaer schrieb:

    D.h. eigentlich fast alles, was man nicht einfach mit 1-2 globalen Variablen (versteckt im namenlosen Namespace im cpp File) auch machen könnte.

    Eine ernst gemeinte Frage: Was kann mit einer Singleton Klasse besser machen als mit ein paar globablen Funktionen?



  • hustbaer schrieb:

    Naja... Logging ist IMO nicht "das Killer Beispiel".

    Jedes Singleton, welches
    a) in Destruktoren anderer Klassen verwendet werden könnte und
    b) selbst irgendwelche "Resourcen" ausser Speicher (File-Handles, DB-Connections etc.) hat
    ist IMO problematisch.

    D.h. eigentlich fast alles, was man nicht einfach mit 1-2 globalen Variablen (versteckt im namenlosen Namespace im cpp File) auch machen könnte.

    Das hätte ich gern näher erklärt. Vor allem der letzte Satz ist mir nicht verständlich. Meiner Meinung nach ist:

    a) Die Verwendung eines Singletons in Destruktoren anderer Klassen kein Problem. Oder jedenfalls kein größeres Problem als die Verwendung irgend eines "Etwas", das Exceptions werfen kann. Und sollte letzteres ein Problem sein, muss der Kunde damit fertig werden (wie immer) und nicht das Singleton.

    Ein Problem tritt vielleicht auf, wenn besagter Kunde eine globale Variable ist (gern auch in Form eines anderen Singletons), deren Destruktor aufgerufen wird, nachdem das verwendete Singleton bereits abgebaut wurde.

    b) Falls ein Singleton Resourcen verwendet, kann das ein Problem sein, sofern das Objekt nicht (s. deinen Vorschlag mit new() ohne delete()) oder zu spät abgebaut wird. Bloß hätte man das Problem nicht ohnehin? Also müsste man bei unserem hypothetischen Logger nicht ohnehin dafür sorgen, dass dieser rechtzeitig seine Datei schließt?

    Stefan.



  • Ben04 schrieb:

    hustbaer schrieb:

    D.h. eigentlich fast alles, was man nicht einfach mit 1-2 globalen Variablen (versteckt im namenlosen Namespace im cpp File) auch machen könnte.

    Eine ernst gemeinte Frage: Was kann mit einer Singleton Klasse besser machen als mit ein paar globablen Funktionen?

    Naja - vor allem ist "Singleton" ein Entwurfsmuster. Also solches wird es praktischerweise als Klasse beschrieben. Ohne Beschränkung der Allgemeinheit würde ich sagen.

    Wenn du nur eine einfache globale Variable per Singleton kapseln willst, mag eine Sammlung von Funktionen in einem gemeinsamen Namespace genügen. Aber praktisch finde ich das nicht, und es wird immer unpraktischer, wenn mehrere Variablen hinzu kommen. Eine Sammlung von Werten, die gemeinsam einen "Status" ausmachen, packt man natürlicherweise in eine Klasse - oder? Und wenn du dann zum Destruktionszeitpunkt noch irgend etwas machen möchtest (den User in der Datenbank abmelden und dann erst die TCP-IP-Verbindung schließen), kommst du um einen Destruktor nicht herum. Bzw. du müsstest bei einem Globale-Variablen-Singleton eine destroy()-Funktion schreiben, die jemand explizit aufruft.

    Stefan.



  • DStefan schrieb:

    Naja - vor allem ist "Singleton" ein Entwurfsmuster.

    Wenn du den Begriff soweit dehnst, dann kannst du quasi alle globalen Funktionen als Singleton auffassen. Nur damit wir nicht aneinander vorbeireden: Ich frage was solche Konstrukte wie im Ursprungspost bringen sollen.

    DStefan schrieb:

    Aber praktisch finde ich das nicht, und es wird immer unpraktischer, wenn mehrere Variablen hinzu kommen.

    Was wird unpraktischer? Ich bin der gegenteiligen Meinung.

    DStefan schrieb:

    Eine Sammlung von Werten, die gemeinsam einen "Status" ausmachen, packt man natürlicherweise in eine Klasse - oder?

    "Natürlich" sind Klassen in der C und C++ Welt nicht. Etwas in eine Klasse zu packen nur damit es eine Klasse ist ist Blödsinn. Wenn es keine Vorteile bringt, dann sollte man den einfachen Weg gehen und der verwendet selten ein class.

    DStefan schrieb:

    Und wenn du dann zum Destruktionszeitpunkt noch irgend etwas machen möchtest (den User in der Datenbank abmelden und dann erst die TCP-IP-Verbindung schließen), kommst du um einen Destruktor nicht herum.

    Stimmt, aber warum kann ich dann nicht einfach ein

    static struct Foo{
      ~Foo(){
        ...
      }
    }foo;
    

    in meine Implementierungsdatei packen? Oder das gute alte atexit?

    DStefan schrieb:

    Bzw. du müsstest bei einem Globale-Variablen-Singleton eine destroy()-Funktion schreiben, die jemand explizit aufruft.

    Was sogar Vorteile hat. Wenns du init und deinit Funktionen manuell aus der main Funktion aufrufst, dann hast du eine saubere Initialisierung und ein sauberes Cleanup. Vorausgesetzt du kriegst die Reihenfolge hin. Wenn nicht, dann kann man mit Referenzzählung nachhelfen.

    Weitere Vorteile meiner Variante:

    • Im Header steht wirklich nur das was man zur Verwendung wissen muss.

    • Man kann sämtliche Implementierungsdetails (wie verwendete Variablen oder Helferfunktionen) ändern und braucht nur die eine cpp neu zu übersetzen.

    • Der Header muss nicht die Header der verwendeten Interna einbinden.

    • Ich muss nur eine Stelle im Code ändern um eine Variable hinzuzufügen. Beim Klassenkonstrukt muss ich sie einmal im Header hinzufügen und einmal in der cpp Datei.

    • Bei neuen Helferfunktionen gilt das genauso.

    • Der Header ist selbstdokumentierend, da das Interessante nicht im Rauschen von Hilfskonstrukten untergeht.

    • ABI unabhängige C-Dll-Schnittstelle kriegt man gratis (falls man zusätzlich auf Parameterüberladung verzichtet).

    • Selbst mit wildem Casten kommt man nicht so einfach an die Interna ran.

    • Bei öffentlichen Funktion brauche ich keine return get_instance()->_get_counter(); Krücke.

    • Man spart sich den ganzen Boilerplate Code um die Klasse unbrauchbar als solche zu machen.

    • Der Code ist simple zu coden. Es gibt wenig Stellen an denen man aus Unwissenheit Fehler machen kann. (Bei der Klassenkrücke gibt es deren sehr viele. Es reicht sich das Beispiel am Anfang dieses Threads anzuschaun.)

    • Es gibt keinen nie verwendeten this Pointer. Der Compiler sollte leicht besseren Code erzeugen können.

    • Kein Witzbold kann versuchen von deinem Singleton abzuleiten.

    • Der Code erfüllt seinen Zweck und sieht elegant aus.

    • Es ist viel weniger Tipparbeit.

    Da ist jetzt nicht der Killervorteil schlecht hin dabei, aber da es gleichzeitig keine mir bekannten Nachteile gibt sehe ich nicht warum man diese Klassenkrücke verwenden sollte.



  • @Ben04
    Du lieferst ganz schön viel Stoff zum Nachdenken 😉 Ich verzichte auf's Zitieren, um meine Antwort nicht unnötig aufzublähen. Sie wird ohnehin groß genug.

    Ich verwende "Objekt" hier im weitest möglichen Sinne, also als "Datenhaufen", wobei die Daten logisch zusammengehören. In diesem Sinne verstehe ich auch eine Ansammlung logisch zusammengehörender Variablen hier mal als "Objekt".

    Also:

    Das Entwurfsmuster Singleton hat das Ziel, sicherzustellen, dass es höchstens eine Instanz eines Objekts geben kann und einen Zugriff auf dieses Objekt zu ermöglichen. Diese Definition ist nicht von mir, sondern von GoF selbst. Ich meine, daran sollte man sich halten. Ich denke nicht, dass jede Sammlung globaler Funktionen in diesem Sinne ein Singleton ist. Allerding kann man durchaus deinen "Namespace-Vorschlag" als Singleton sehen.

    Wenn du die Verwendung von Klassen nicht praktischer findest als einen Haufen Daten, die zusammen ein Objekt (in obigem Sinne) ergeben, ist das deine Sache. Für mich aber ist die Verwendung von Klassen sowohl praktischer als auch natürlicher. Ich möchte gern direkt im Quelltext lesen, dass die Variablen name , geburtsdatum und adresse eine Person definieren. Deshalb fasse ich diese Daten in einer Klasse zusammen. Ich möchte außerdem gern die Automatismen für Ctor und Dtor nutzen, so dass man Objekt garantiert initialisiert ist und auch korrekt wieder abgebaut wird. Deshalb ist die Definition einer Klasse für mich natürlicher.

    Ziemlich unnatürlich finde ich hingegen deine Definition dieses Foo-Objekts, das nichts weiter macht als aufräumen. Wenn man schon mal soweit ist, warum nicht alles in eine Klasse packen?

    Klar, warum nicht atexit()? Bloß stellt sich mir die Frage halt anders herum: Warum denn? Wenn ich ein einfaches Singleton habe, für das ich glaube, dass es beim Abschied des Programms keine Probleme macht - warum dann nicht den Destruktor verwenden und den Compiler den Job machen lassen? Das finde ich deutlich naheliegender.

    Die Verwendung von init() und destroy() für ein Singleton hat den großen Nachteil, dass man es explizit machen muss. Vergisst das jemand in main(), kann es Probleme geben. Verwendet man Ctor und Dtor, gibt es diese Probleme nicht.

    Du hast Recht, die Deinitialisierung eines Singletons kann problematisch sein. Darüber gibt es ja auch reichlich Literatur. Das Problem wird allerdings nicht gelöst, wenn man in main() destroy() aufruft, weil nämlich auch nach Beendigung von main() noch Code ausgeführt wird, und dieser Code kann versuchen, das Singleton zu verwenden, nachdem es bereits abgebaut wurde.

    Du hast wieder Recht, wenn du sagst, dass man mit deiner Methode bei Änderungen in der Implementierung unter Umständen weniger abhängige Dateien übersetzen muss. Allerdings finde ich diesen Punkt vernachlässigbar, und er ist auch nicht für Singeltons spezifisch. Wenn ich irgend etwas so programmiere, wie du es vorschlägst, kann ich vermutlich Compile-Zeit sparen. Aber wollen wir hier wirklich über die Vor- und Nachteile objektorientierter Programmierung reden?

    Kein Header ist von sich aus selbstdokumentierend, keine Technik sorgt von sich aus für mehr (oder eben weniger) Übersicht. Auch Klassen verursachen nicht mehr oder weniger "Rauschen", als eine Sammlung von Funktionen in einem Namespace. Rauschen verursacht oder vermeidet der Entwickler.

    Deine Behauptung, man erhalte eine ABI-unabhängige Schnittstelle ist mE falsch. Der Namespace geht mit in das Name Mangling ein und das Ergebnis ist keine C-Schnittstelle. Jedenfalls machen das meine Compiler so - und ich glaube, sie müssen das auch machen.

    "Wildes Casten" oder mutwillige Änderungen an Headern mit dem Ziel an ursprünglich private Daten zu kommen, sind nichts, mit dem ich mich beschäftige. Wer sowas macht, ist selbst Schuld. Meinetwegen gerne!

    Eine mögliche Arbeitseinsparung mit deiner Vorgehensweise ist doch kaum der Rede wert. Selbst wenn ich ein einfaches Singleton komplett von Hand schreibe, benötige ich für die Infrastruktur (also den Singleton-Code im engeren Sinne) kaum eine viertel Stunde. Aber idR schreibe ich diesen Code gar nicht von Hand. Ich verwende ein Template, dass die Infrastruktur bereits für mich erledigt und konzentriere mich auf die Klasse selbst.

    In diesem Sinne ist deine Version auch nicht simpler zu schreiben. Aber sie ist schwieriger zu verwenden. Denke nur an init() und destroy().

    Ich glaube nicht, dass der nicht vorhandene this-Pointer dem Compiler ermöglicht "besseren" Code zu erzeugen. In wiefern denn besser? Was meinst du damit? Wenn ich mir eine Singleton-Klasse vorstelle, deren Methoden größtenteils inline sind, fällt es mir schwer vorzustellen, wie ein Compiler aus globalen Funktionen, die auf cpp-lokale Variablen zugreifen mehr herausholen könnte.

    Ein Witzbold, der von meinem Singleton ableitet, weiß entweder, was er tut oder er kann mir gestolen bleiben. Das ist doch kein Argument gegen die Verwendung von Klassen. Außerdem wird ggf. er sowieso von Fehlermeldungen überschüttet werden und schnell wieder zu Verstand kommen. Andererseits könnte ich aber Ableitungen ermöglichen - aus welchem Grund auch immer - oder sogar den Einsatz einer Unterklasse bei der Verwendung des Singletons, etwa über eine Factory.

    Stefan.



  • DStefan schrieb:

    Ziemlich unnatürlich finde ich hingegen deine Definition dieses Foo-Objekts, das nichts weiter macht als aufräumen. Wenn man schon mal soweit ist, warum nicht alles in eine Klasse packen?

    Wegen der 15 Gründe als meinem vorherigen Post? Der Punkt ist der, dass ich noch kein einziges Argument für die Verwendung der Klassenkrücke gesehen habe. Du schwächste mit mehr oder weniger Erfolg meine Argumente ab. Entkräftete wird wird aber keins. Die einzig logische Schlussfolgerung ist diese Krücke nicht zu verwenden.

    DStefan schrieb:

    Die Verwendung von init() und destroy() für ein Singleton hat den großen Nachteil, dass man es explizit machen muss. Vergisst das jemand in main(), kann es Probleme geben. Verwendet man Ctor und Dtor, gibt es diese Probleme nicht.

    Ctor und Dtor funktionieren nur falls sämtliche Interaktion zwischen den Singletons nur innerhalb der main Funktion (oder von da aufgerufenen Funktionen) stattfindet. Wenn das nicht der Fall ist geht der Dtor Ansatz nicht wie du bereits selbst gezeigt hast.

    DStefan schrieb:

    Das Problem wird allerdings nicht gelöst, wenn man in main() destroy() aufruft, weil nämlich auch nach Beendigung von main() noch Code ausgeführt wird, und dieser Code kann versuchen, das Singleton zu verwenden, nachdem es bereits abgebaut wurde.

    Hausaufgabe 1: Überlege dir warum du hier versuchst ein Nonproblem zu lösen. (Tipp: Wir mischen keine Singletontypen)
    Hausaufgabe 2: Überlege dir wie man selbst beim Mischen verschiedener Singletontypen und nicht zyklischer Interaktionen das "Problem" löst. (Tipp: Referenzzählung)

    DStefan schrieb:

    Wenn ich irgend etwas so programmiere, wie du es vorschlägst, kann ich vermutlich Compile-Zeit sparen.

    und Entwickelungszeit da der Code dem KISS-Prinzip folgt, Hirnschmalz beim verstehen dieser Klassenkrücken und ein klein wenig Laufzeit.

    DStefan schrieb:

    Aber wollen wir hier wirklich über die Vor- und Nachteile objektorientierter Programmierung reden?

    Bleibt mal beim konkreten Fall.

    DStefan schrieb:

    Kein Header ist von sich aus selbstdokumentierend, keine Technik sorgt von sich aus für mehr (oder eben weniger) Übersicht.

    Also wenn ich

    #ifndef RANDOM_NUMBER_GENERATOR_HPP
    #define RANDOM_NUMBER_GENERATOR_HPP
    namespace random{
      void seed(int n);
      int roll();
    }
    #endif
    

    lese, dann kann ich mir die Doku sparen.

    DStefan schrieb:

    Auch Klassen verursachen nicht mehr oder weniger "Rauschen", als eine Sammlung von Funktionen in einem Namespace. Rauschen verursacht oder vermeidet der Entwickler.

    Dann übersetzt mein Beispiel oben mal in eine korrekte Klassenkrücke, ohne dass mind. 50% deiner Codes 0 nützliche Informationen an den Benutzer liefert.

    DStefan schrieb:

    Deine Behauptung, man erhalte eine ABI-unabhängige Schnittstelle ist mE falsch. Der Namespace geht mit in das Name Mangling ein und das Ergebnis ist keine C-Schnittstelle. Jedenfalls machen das meine Compiler so - und ich glaube, sie müssen das auch machen.

    Du wirst doch noch fähig sein ein extern "C"{ ... } drum rum zu packen, wenn du das wirklich brauchst. Der Punkt ist, du musst 12 Zeichen einfügen um etwas zu erreichen, was bei der Klassenkrücke nur mit großen Mühen vielleicht machbar ist.

    Eine mögliche Arbeitseinsparung mit deiner Vorgehensweise ist doch kaum der Rede wert. Selbst wenn ich ein einfaches Singleton komplett von Hand schreibe, benötige ich für die Infrastruktur (also den Singleton-Code im engeren Sinne) kaum eine viertel Stunde.

    Ich hab für den Code oben nicht mal eine Minute gebraucht. Für eine korrekte Klassenkrücke bräuchte ich wohl etwa 10 Minuten. Das sind 90% der Zeit gespart und viele Möglichkeiten weniger Bugs einzubauen.

    In diesem Sinne ist deine Version auch nicht simpler zu schreiben. Aber sie ist schwieriger zu verwenden. Denke nur an init() und destroy().

    Dass du auch auf die Ctor- und Dtorschiene mit all ihren Problemen aufspringen kannst habe ich bereits gezeigt. Ansonsten ist die syntaktische Verwendung bis aufs letzte Zeichen gleich und kann deswegen gar nicht schwieriger sein. Du kreidest hier an, dass man init() und destroy() verwenden kann und somit ein sauberes Shutdown realisieren kann.

    Ich glaube nicht, dass der nicht vorhandene this-Pointer dem Compiler ermöglicht "besseren" Code zu erzeugen.

    Sollen wir wetten, dass da ein Parameter weniger auf dem Stack oder in einem Register übergeben wird?

    deren Methoden größtenteils inline sind

    Sind sie aber nicht, siehe Ursprungspost.

    Ein Witzbold, der von meinem Singleton ableitet, weiß entweder, was er tut oder er kann mir gestolen bleiben. Das ist doch kein Argument gegen die Verwendung von Klassen.

    Natürlich ist es eins. Ein sehr schwaches aber es ist ein Argument.

    Andererseits könnte ich aber Ableitungen ermöglichen - aus welchem Grund auch immer - oder sogar den Einsatz einer Unterklasse bei der Verwendung des Singletons, etwa über eine Factory.

    Wat?Ich kann dir da nicht folgen. Denk dein Argument noch mal durch und überlege dir, ob es dann auch wirklich nur max. 1 Objekt gibt. Ich würde so weit gehen zu sagen, dass von einem Singletonableiten immer Blödsinn ist. Begründung:
    * Es gibt eine global zugängliche Instanz der Basisklasse.
    * Es darf maximal eine Instanz der Basisklasse geben.
    * Jede abgeleitete Klasse erzeugt bei der Instantiierung eine Instanz der Basisklasse.
    => Die abgeleitete Klasse darf nie instantiiert werden.
    => Sie ist Blödsinn.


Anmelden zum Antworten