Liste mit Refenzen von Objekten



  • Hi,

    wie löst ihr das wenn ihr eine Liste mit Referenzen auf Objekten braucht. Also beispielsweise beim Oserver-Pattern, in dem ja im Subject einen Liste der registrierten Observer gespeichert werden. Ich habe das mit einem Pointern und einem Vector gelöst. In einem anderen Thread wurde gesagt das man Pointer sehr selten in C++ braucht und da ich mich gerade ein wenig mit Design Pattern beschäftige nutze ich Pointer sehr oft.

    Ich habe hier keine Tests drinne oder sonst welche Sicherungen, es dient nur dazu ein paar Pattern mal selbst auszuprobieren.

    // kontretes Subjekt
    class Subject : public ISubject
    {
    private:
        std::vector<IObserver*> observers;
        std::vector<IObserver*>::iterator it;
        int data;
    public:
        void setData(int data)
        {
            this->data = data;
            notifyObserver();
        }
    
        void addObserver(IObserver* observer)
        {
            observers.push_back(observer);
        }
    
        void deleteObserver(IObserver * observer)
        {
            it = find(observers.begin(), observers.end(), observer);
            observers.erase(it);
        }
    
        void notifyObserver()
        {
            for(it = observers.begin(); it < observers.end(); ++it)
            {
                (*it)->update(data);
            }
        }
    };
    


  • Wieso ist der iterator Member der Klasse? Bedenke, dass ein Observer sich vielleicht in Folge eines notify() direkt oder indirekt selbst aus der Liste austragen oder einen weiteren Observer eintragen könnte, wodurch alle Iteratoren in den vector ungültig werden (in deinem Fall überschreibst du auch noch it in deleteObserver()). Bedenke auch, dass addObserver() und deleteObserver() evtl. threadsafe sein sollten...



  • Ok, Zeiger sind in dem Fall schon angebracht. Ich habe auch noch eine Seite mit interessanten Links dazu gefunden: http://programmers.stackexchange.com/questions/125496/observer-pattern-knowing-what-changed



  • In C++ hat man die Möglichkeit, so etwas sinnvoll zu abstrahieren. Da landet man dann bei boost::signal und kommt in der Tat nicht mehr wirklich mit Zeigern in Berührung.

    Da Zeiger ein ziemliches Low-Level-Mittel sind und gute Programmierer möglichst High-Level programmieren, werden Zeiger selten direkt verwendet.

    Observer mit Interfaces zu implementieren ist vielleicht in Java üblich, aber C++ bietet da ganz andere Möglichkeiten. Man möchte eigentlich nur eine Funktion angeben, die bei dem Ereignis aufgerufen wird. Da es in Java keine Funktionszeiger gibt, geht man den Umweg über Interfaces. In C++ kann man direkt eine Funktion angeben (Funktionszeiger, Lambda, Funktor) oder aus einer Methode erstellen ( std::bind ). Der Zeiger auf das zu benachrichtigende Objekt wird einmal mit bind gebunden und wird dann nie wieder direkt verwendet. Das Abmelden läuft typischerweise automatisch mit einem Helfer wie scoped_connection .

    #include <boost/signal.hpp>
    #include <iostream>
    
    struct Subject
    {
    	boost::signal<void (int)> dataChanged;
    
    	void setData(int data)
    	{
    		m_data = data;
    		dataChanged(m_data);
    	}
    
    private:
    
    	int m_data = 0;
    };
    
    struct SubjectObserver
    {
    	explicit SubjectObserver(Subject &observed)
    		//meldet sich mit seiner Methode bei observed an
    		: m_dataChangedConn(observed.dataChanged.connect(
    			boost::bind(&SubjectObserver::onDataChanged, this, _1)))
    	{
    	}
    
    private:
    
    	boost::signals::scoped_connection m_dataChangedConn;
    
    	void onDataChanged(int data)
    	{
    		std::cout << data << "\n";
    	}
    };
    
    int main()
    {
    	Subject s;
    
    	{
    		SubjectObserver observer(s);
    
    		s.setData(123);
    
    		//observer meldet sich automatisch ab durch m_dataChangedConn
    	}
    
    	s.setData(456);
    }
    
    //Ausgabe:
    //123
    
    //(ungetestet)
    

    Einmal wird this angegeben, danach werden keine Zeiger mehr benutzt.
    Man braucht in C++ also selbst bei komplexen Vorgängen oft keine direkten Zeiger.



  • Danke für die Tipps. Tja an so Sachen wie Threadsafe oder dass ich mir was zerschießen könnte, habe ich überhaupt nicht gedacht. Mir war erst mal wichtig die Pattern überhaupt zu verstehen. Abstrakte Klassen als Interfaces sieht man halt häufig im Netz wenn Design Pattern erklärt werden, da glaubt man erst mal dass das so einigermaßen korrekt ist.

    Mit Boost bin ich noch gar nicht in Berührung gekommen.



  • TyRoXx schrieb:

    Da Zeiger ein ziemliches Low-Level-Mittel sind und gute Programmierer möglichst High-Level programmieren, werden Zeiger selten direkt verwendet.

    Wie kommst du darauf ?



  • Frage4234 schrieb:

    TyRoXx schrieb:

    Da Zeiger ein ziemliches Low-Level-Mittel sind und gute Programmierer möglichst High-Level programmieren, werden Zeiger selten direkt verwendet.

    Wie kommst du darauf ?

    Wie kommst du darauf, dass dem nicht so ist?



  • Nach der Aussage wäre ja jeder Assembler-Programmierer schlechter als jeder Hochsprachen-Programmierer.



  • Die Aussage ist imo auch Schwachsinn. Einen guten Programmierer erkennt man, wie auch einen guten Schauspieler, an einem: Range... 😉



  • Eisflamme schrieb:

    Nach der Aussage wäre ja jeder Assembler-Programmierer schlechter als jeder Hochsprachen-Programmierer.

    Nö.

    dot schrieb:

    Die Aussage ist imo auch Schwachsinn. Einen guten Programmierer erkennt man, wie auch einen guten Schauspieler, an einem: Range... 😉

    Echte Programmierer arbeiten mit kosmischer Strahlung und Schmetterlingen oder wie war das?



  • Gibt es eigentlich ein gutes Buch über Design Pattern und dessen Implementierungen in C++, wo dann elegantere Methoden als Zeiger verwendet werden?
    Boost muss ich mir echt mal anschauen, scheinen ja sehr schmackhafte Happen drin zu sein.

    EDIT: Ich habe mal die Boostlibs ein wenig überflogen, was haltet ihr von diesem Container(http://www.boost.org/doc/libs/1_51_0/libs/ptr_container/doc/ptr_container.html) für die häufige Notwendigkeit von Pointerlisten in DesignPattern?



  • Es gibt da auch noch std::reference_wrapper (C++11, auch in Boost mit anderem namespace), nur so nebenbei angemerkt.



  • Danke für den Tipp. Mit C++11 werden ja die Quellcodes doch ein wenig anders aussehen lassen, allein schon einfach auto schreiben zu können und der Compiler nimmt dann Beispielsweise gleich den richtigen Iterator, oder die Initialisierung mit {} usw.

    Nutzt ihr schon viel C++11 bei neuen Projekten?



  • Butterbrot schrieb:

    Nutzt ihr schon viel C++11 bei neuen Projekten?

    Ich zähl wahrscheinlich nicht, weil ich nicht in einer Firma arbeite (wo ganz andere Regeln bezüglich des Compiler-Umstiegs gelten usw.), aber ja.

    Edit: Nimm bitte deine Signatur raus, ich schreib doch nicht dasselbe in meine 😉



  • Natürlich zählst du, da du in C++ entwickelst. Ob das nun kommerziell, privat, kleines Projekt, großes Projekt ist, ist Nebensache.

    Firmen sind eh immer Jahre hinter der Entwicklung hinterher, müssen sie auch teilweise wegen der Kompatibilität der eigenen Produkte sein.

    Wir in der Firma haben noch Restcode aus einen ganz alten Zend-Framework drinne. Theoretisch hört es sich toll an, wenn man mit Frameworks arbeitet dann tauscht man nur das Framework aus und fertig ist. In der Praxis musste man doch die eine oder andere Anpassung machen und schon ist eine bestimmte Version des Frameworks fest verdrahtet.



  • TyRoXx schrieb:

    Eisflamme schrieb:

    Nach der Aussage wäre ja jeder Assembler-Programmierer schlechter als jeder Hochsprachen-Programmierer.

    Nö.

    Na ja, nicht in Relation gesetzt und "möglichst" als oberstes Ziel verstanden, schon. Aber ich weiß ja eigentlich, was Du meinst, und Anfänger werden es wohl auch verstehen, also halt ich Mal die Klappe. 🤡



  • Hehe, in jedem Forum das gleiche, es ist schwer einen Thread sachlich zu halten^^


Log in to reply