static map<string, singleton*> als Singleton-Registry crasht beim bevölkern



  • Hallo alle.

    Frei nach gang of four hab ich eine Singleton Klasse implementiert, die eine registry für Subklassen benutzt. Konkret gehts um Netzwerke (im Sinn von Graphen), daher sieht die abstrakte Basisklasse so aus, reduziert aufs wesentliche:

    Header:

    class network {
          public:
          virtual ~network();
    
          static network * instance();
          static void registerNetworkSubclass(string s, network * newNet);
    
          protected:
          network();
          static network * lookupNetworkSubclass(const string& name);
    
          private:
          static network * networkInstance;
          static map<string, network*> theNetworkRegistry;
    
          virtual void buildNetworkToSpecification() = 0;
    };
    

    Implementierung:

    network * network::networkInstance = 0;
    map<string, network*> network::theNetworkRegistry;
    
    network * network::instance(){
            if(!networkInstance){
                string networkSubclassName = "ERNetwork"; // <- soll später durch user input festgelegt werden
                networkInstance = network::lookupNetworkSubclass(networkSubclassName);
            }
            return networkInstance;
    }
    
    void network::registerNetworkSubclass(string newString, network* newNet){
         network::theNetworkRegistry[newString] = newNet;
    }
    
    network * network::lookupNetworkSubclass(const string& name){
         //note: if 'name' is not recognized, a new map entry will be created containing (name, 0 (default pointer value))
         return theNetworkRegistry[name];
    }
    

    So weit so gut. Eine abgeleitete Klasse (für Erdös-Renyi Graphen) sieht so aus:
    Header:

    class ERNetwork : public network{
    
          public:
          ERNetwork();
          ~ERNetwork();
    
          private:
          void buildNetworkToSpecification();
    };
    

    Implementierung:

    static ERNetwork theERInstance; //<- eine static instanz der subklasse, damit sie sich via konstruktor registiert
    
    ERNetwork::ERNetwork() : network(){
            network::registerNetworkSubclass("ERNetwork", this);
    }
    
    ERNetwork::~ERNetwork(){}
    

    Wenn ich jetzt mein Programm starte, wird die statische Instanz der Subklasse erzeugt, die sich daraufhin registrieren will. Innerhalb der Funktion 'network::registerNetworkSubclass' schmiert das Programm mit einer Speicherzugriffsverletzung ab (glaub ich - unter Windows sehen diese Fehlerwindows alle gleich aus), wenn ich das neue (name, network*) Paar hinzufügen will.

    Gibt es eine Besonderheit mit maps als statische objekte, oder was mach ich falsch?


  • Mod

    network::theNetworkRegistry muss initialisiert werden, bevor theERInstance initialisiert wird. Falls du richtigerweise beide Objekte in verschiedenen ÜEs definiert hast, ist die Reihenfolge ihrer Initialisierung aber unspezifiziert. Die einfachste Lösung dürfte wohl sein, network::theNetworkRegistry durch eine entsprechende FUnktion zu ersetzen, die bei Bedarf initialisiert, also

    static map<string, network*>& registry() {
        static map<string, network*> registry;
        return registry;
    }
    


  • Ah, tricky.

    Danke für die schnelle Antwort!

    Auf zum nächsten Crash... aber ich versuch mich erst selbst dran, bevor ich weiterfrage.



  • Ok, ich kriegs immer noch nicht gebacken.

    Mit campers Variante funktioniert mein Programm jetzt so weit dass sich alle (zwei) Subklassen mittels ihrer jeweiligen static Instanzen registrieren, ABER die registry ist nicht persistent... wenn ich versuche, auf die map zuzugreifen, ist sie immer wieder leer. Sowohl wenn sich eine zweite Subklasse registriert, als auch wenn ich im Hauptprogramm per instance() auf mein (unique) network zugreifen will.



  • Mein Fehler:

    Man sollte besser schreiben

    void network::registerNetworkSubclass(string newString, network* newNet){
    map<string, network*>& reg = network::registry();
    reg[newString] = newNet;
    }

    anstelle von

    void network::registerNetworkSubclass(string newString, network* newNet){
    map<string, network*> reg = network::registry();
    reg[newString] = newNet;
    }

    da man sonst immer nur auf einer lokalen Kopie die Schlüsseleinträge hinzufügt.


Anmelden zum Antworten