große Vektoren überladen


  • Mod

    Überladen von Operatoren für Standardtypen ist ein tolles Rezept für unerwartete Probleme. Besonders wenn man noch nicht einmal selber Schleifen programmieren kann…

    Was hat die Frage überhaupt mit Überladung zu tun?



  • @DocShoe Ich kenne den Talk und glaube nicht, dass er dagegen ist. Gegenfrage: kennst du die Slide 39 aus dem Talk?


  • Mod

    Slide 4 sagt doch schon direkt am Anfang "A raw loop is any loop inside a function where the function serves purpose larger than the algorithm implemented by the loop". Wenn die Funktionalität ist "addiere zu jedem Element einen Wert", dann gibt es keinen larger purpose jenseits der Schleife. Ich bin hier ganz bei wob.



  • Nur der Vollständigkeit halber sei noch std::valarray erwähnt:

    #include <valarray>
    int main()
    {
      std::valarray<int> v={0, 1, 2, 3};
    
      v+=1; // alle Elemente um 1 erhöhen
    }
    

    Ob es passt, weiß ich natürlich nicht (ich verwende einen std::vector auch lieber).



  • @wob sagte in große Vektoren überladen:

    Und ja, ich finde das einfacher & deswegen besser als hierfür einen std-Algorithmus zu nehmen.

    Ein paar Überlegungen dazu (allgemeiner Art, nicht auschliesslich auf transform bezogen):

    Man könnte argumentieren dass die Implementierung von transform-ähnlichen Algorithmen mehr Freiheiten hat, die Gesamtoperation auf verscheidene Art und Weise durchzuführen, wodurch sich mehr Optimierungspotential ergibt - wenn denn eine Bibliothek den Aufwand betreiben will - wenn nicht, dann ist es trotzdem mindestens so gut wie der handgeschriebene Loop.

    Das liegt vor allem daran, dass ein ganzer Bereich statt nur einzelne Elemente übergeben werden. Theoretisch könnte so ein transform-ähnlicher Argorithmus z.B. den Bereich der Elemente partitionieren und die Opratationen parallel ausführen. Oder z.B. interne SIMD-Optimierungen durchführen, wenn z.B. die Art der Operation wie increment_one bekannt ist, die Addition für meherere Elemente gleichzeitig mittels Vektorinstruktionen der CPU durchführen.

    Auch muss z.B. der an increment_one übergebene int& theoretisch nicht einmal das tatsächliche Range-Element referenzieren, sondern es kann z.B. auch ein temporäres oder gecachtes Objekt sein, das aus irgendeinem Grund in diesem Kontext Sinn macht.

    Bei der for-Schleife gibt es diese Optimierungsmöglichkeiten für die Bibliotheks-Entwickler nicht. Da kann das höchstens der Compiler machen.



  • @Finnegan sagte in große Vektoren überladen:

    Theoretisch könnte so ein transform-ähnlicher Argorithmus z.B. den Bereich der Elemente partitionieren und die Opratationen parallel ausführen.

    Der Standard definiert bereits eine (potentiell) parallele transform-Version.



  • Also erstmal vielen Dank für die schnellen und vielen Antworten. Ich versuche gleich mal die Vorschläge in die Tat umzusetzen.

    Hier einfach mal die Aufgabenstellung. Das soll kein Aufruf zu einer Lösung sein, sondern einfach das Problem veranschaulichen:

    Eine Klasse Signal, die eine Folge von Messwerten repräsentiert. Die Anzahlder Messwerte sei daher zunächst unbekannt. Signale müssen sich aus einspaltigen Textdateien einlesen
    lassen, die Dezimalzahlen enthalten. Signale müssen sich in einspaltige Textdateien speichern lassen. In beiden Fällen muss der Dateiname frei festlegbarsein. Die Signalklasse muss Methoden enthalten, um Anzahl, Minimum, Maximum, Summe, Mittelwert und Standardabweichung der Messwerte zu berechnen. Es muss möglich sein, Signalen einen konstanten Dezimalwert zu addieren und zu subtrahieren sowie sie mit einem konstanten Dezimalwert zu multiplizieren. Geeignete Operatoren sollten hierfür überladen sein. Es muss möglich sein, das Signal durch einen Moving-Average-Filter wählbarer Breite zu glätten.
    (Zitat aus der Aufgabenstellung)

    Meint ihr, ich habe das Problem richtig verstanden, dass ich auf jede Zahl im Vektor mit HIlfe eines überladenen Operanden jeweils einen int wert addieren/subtrahieren/multiplizieren können soll?

    Also sprich "vektor a + 10"?

    Cheers!



  • @wob sagte in große Vektoren überladen:

    Was spricht gegen eine einfache for-Loop?
    Und ja, ich finde das einfacher & deswegen besser als hierfür einen std-Algorithmus zu nehmen.

    Seh ich genauso. Und wenn man unbedingt einen std::algrithm nehmen will, dann reicht hier ein Lambda. Da muss man nicht noch was außerhalb der Funktion definieren.


  • Mod

    @Calumeth sagte in große Vektoren überladen:

    Meint ihr, ich habe das Problem richtig verstanden, dass ich auf jede Zahl im Vektor mit HIlfe eines überladenen Operanden jeweils einen int wert addieren/subtrahieren/multiplizieren können soll?

    Also sprich "vektor a + 10"?

    Nicht ganz. Gemeint ist objekt_von_deiner_signalklasse + 1.



  • Ah okay! Wie bei Java damals. Ich instanziiere also von außerhalb ein Objekt meiner Signalklasse, und soll dann mit den Rechenoperatoren +,-,* damit rechnen können. In diesem Abbild meiner Klasse sind auch meine eingelesenen Werte vorhanden (im Vektor gespeichert), auf die sich die Rechnungen auswirken sollen.
    So macht es in meiner Welt gerade Sinn, ....oder?



  • ja.



  • Okay, super! ich versuche jetzt mal alles zu ordnen und auch schon zu programmieren. So ganz hatte ich Überladung wohl doch noch nicht verstanden. Ich melde mich nochmal bei, wenn ich einen Schritt weitergekommen bin.
    Vielen, vielen Dank schonmal für eure Hilfe!!



  • @Calumeth sagte in große Vektoren überladen:

    Wie bei Java damals. Ich instanziiere also von außerhalb ein Objekt meiner Signalklasse, und soll dann mit den Rechenoperatoren +,-,* damit rechnen können.

    Also wenn Java Operator Overloading unterstützt, dann hab ich da gewaltig 'was verpasst.
    (Nicht dass ich Java verwenden würde, aber so gundlegende Dinge interessieren mich dann schon, auch bei Sprachen die ich verabscheue.)



  • Na komm, ob nun object.add(other); oder object += other; macht doch keinen so großen Unterschied.

    Nachdem ich mal 9 Monate Java gemacht hatte (länger hab ich Webshops & Co nicht ausgehalten), fehlten mir in C++ zwei Dinge:

    • enum classes. In Java kann man enums mit Objekten haben, die dann z.B. bestimmte getter haben. Das ist sehr sehr praktisch. In C++ müsste man dann x static Objekte einer Klasse anlegen und irgendwie zusehen, dass der Konstruktor nicht öffentlich ist. Sehr unschön.
    • In Java läuft der Constructor erst, wenn das Objekt seinen finalen Typ hat, d.h. ich kann dort auch bereits virtuelle Funktionen aufrufen. In C++ geht das so nicht, weil der Constructor dafür "zu früh" läuft. Vielleicht war das Pattern nicht optimal, im Constructor bereits virtuelle Funktionen aufzurufen, aber das fehlte mir in C++ eine Zeit lang deutlich.
    • generell hatte ich in Java das Gefühl, dass wir ständig hunderte Klassen geschrieben haben, die eigentlich nur irgendwas durchgereicht haben und man beim Debuggen von einer Klasse zur nächsten gekommen ist, ohne dass eigentlich irgendwas passiert wäre.
    • Java kompiliert "etwas" schneller als C++

    Das ist sicher sehr subjektiv und es gibt viel mehr. Aber das sind die Punkte, an die ich mich am meisten erinnere.



  • @wob sagte in große Vektoren überladen:

    Na komm, ob nun object.add(other); oder object += other; macht doch keinen so großen Unterschied.

    Also für mich macht es schon einen Unterschied ob Strings mittels

    str1 == str2
    

    oder

    str1.equals(str2)
    

    verglichen werden. Compiliert nämlich leider beides, nur das erste vergleicht eben nur den Inhalt der Referenzen und nicht der Objekte selbst. Gerade fehlendes operator Overloading lässt mich Java ziemlich hassen (nicht nur das, ist aber auch ein riesen Punkt).

    Werde ja gerade durch die Uni dazu gezwungen mir Java anzueignen. Das ist mit einem Background von knapp 15 Jahren c++ jetzt kein Hexenwerk aber bringt einen auch teilweise echt zum Verzweifeln^^

    Dazu gesellen sich dann noch solche Dinge wie fehlender impliziter copy-support, DTors, const-correctness, generelle value semantics von Objekten, etc.



  • @hustbaer sagte in große Vektoren überladen:

    @Calumeth sagte in große Vektoren überladen:

    Wie bei Java damals. Ich instanziiere also von außerhalb ein Objekt meiner Signalklasse, und soll dann mit den Rechenoperatoren +,-,* damit rechnen können.

    Also wenn Java Operator Overloading unterstützt, dann hab ich da gewaltig 'was verpasst.
    (Nicht dass ich Java verwenden würde, aber so gundlegende Dinge interessieren mich dann schon, auch bei Sprachen die ich verabscheue.)

    Das war aufs instanziieren bezogen. Unglücklich ausgedrückt.



  • Ich habs jetzt hinbekommen, und wollte eben meine Lösung kundtun. Vielleicht steht ja irgendwann jemand vor einem ähnlichen Rätsel.

    Main:

    #include <iostream>
    #include "signal.cpp"
    
    Signal sig;
    
    int main()
    {
    	sig = Signal();     //erstellt ein Objekt der Klasse Signal
    	sig.file_read();    //liest die Signalwerte ein
    	sig + 5;            //addiert jedem Wert im Vektor +5 dazu 
    }
    

    Signalklasse:

    #include <iostream>
    #include <fstream> 
    #include <vector>
    
    class Signal
    {
    public:
        Signal();
        std::vector<std::string> herzfrequenzen; //hier werden String eingelesen
        std::vector<double> herzfrequenzen_int; //hier werden Strings umgewandelt in doubles gespeichert
        inline int file_read(); //Methode zum einlesen einer Datei
        inline void operator+(int addition); //Methode zum Überladen des "+"Operanden
    };
    
    inline Signal::Signal() {}
    
    void Signal::operator+(int addition)
    {
        for (int i = 0; i < herzfrequenzen_int.size(); i++)
        {
            herzfrequenzen_int.at(i) = herzfrequenzen_int.at(i) + addition;
        }
    }
    

    Ich habe mein Problem jetzt mit einer einfachen for-Schleife gelöst, die jeden Wert des Vektors mit dem gewünschten addiert.

    Beste Grüße und vielen Dank an alle!


  • Mod

    @Calumeth sagte in große Vektoren überladen:

    Signal sig;
    
    int main()
    {
    	sig = Signal();     //erstellt ein Objekt der Klasse Signal
    

    Du hast zu viel Java gemacht. Würdest du auch folgendes schreiben?

    int i;
    
    int main()
    {
    	i = int();     //erstellt ein Objekt der Klasse int
    

    Funktioniert zwar, aber ziemlich dämlich, oder? Das ist nicht anders, egal ob die Klasse int oder Signal ist.


    Weiter:

        std::vector<std::string> herzfrequenzen; //hier werden String eingelesen
    

    Nein, da werden keine Strings gelesen. Schreib keinen Quatsch in Kommentaren. Oder wenn du denkst, der Kommentar wäre richtig: Er ist Quatsch.

    Die Strings gehören gar nicht als Member in die Klasse, weil es ja nur eine einzige Methode geben sollte, die sie nutzt. Und in dieser Methode brauchst du auch keinen Vector an Strings, denn du kannst ja gleich deine Werte einlesen.


        std::vector<double> herzfrequenzen_int; //hier werden Strings umgewandelt in doubles gespeichert
    

    Siehe oben zum Kommentar.


        inline int file_read(); //Methode zum einlesen einer Datei
    

    Inline? Int? Was geht hier vor? Und woher wird gelesen?
    Außerdem: Wenn es doch bloß eine Methode gäbe, wie man universell aus jeder beliebigen streamartigen Quelle lesen (und schreiben) könnte. Dann wären alle Probleme automatisch erledigt. Könnte man vielleicht im Kapitel über Operatorüberladung unterbringen so eine Methode, weil das gewiss sehr schön ginge damit…


        inline void operator+(int addition); //Methode zum Überladen des "+"Operanden
    

    Inline? Ok, macht vielleicht schon eher Sinn hier. void? Das Ergebnis einer Addition ist ein void? Und was ist das für eine Parametername? Weiß ja jeder aus der Mathematik, wenn man 5 + 6 schreibt, dann nennt man die 5 und die 6 die "Additionen" 🙄


    inline Signal::Signal() {}
    

    Wozu ist das da?


    void Signal::operator+(int addition)
    {
        for (int i = 0; i < herzfrequenzen_int.size(); i++)
        {
            herzfrequenzen_int.at(i) = herzfrequenzen_int.at(i) + addition;
        }
    }
    

    Super. Weiß ja jedes Kind, wenn man 5+6 schreibt, dann ist das Ergebnis void, aber die 5 ist hinterher 'ne 11.



  • @SeppJ sagte in große Vektoren überladen:

    Weiß ja jeder aus der Mathematik, wenn man 5 + 6 schreibt, dann nennt man die 5 und die 6 die "Additionen" 🙄

    @Calumeth Lass' Dich nicht von SeppJ veräppeln, sie heißen natürlich Additoren!



  • @Calumeth Die Dinger heißen natürlich Summand. Allerdings sind gerade bei Operatoren zum Beispiel auch Namen wie "lhs" und "rhs" für "left/right hand side", also das Ding links und rechts vom Operator, üblich. Bei einem einfachen Wert für eine Summe könnte man das auch value nennen. Nur ist "Addition" ein Nomen, das nicht zu einem Wert passt. (allerdings sind alle anderen Kommentare von @SeppJ viel wichtiger, versuche die zu verstehen!)

    Du solltest noch einmal überlegen, ob du auch 5 + sig statt nur sig + 5 rechnen können willst. Du hast hier nicht +, sondern eine Art von += implementiert, die du aber + genannt hast. Schau dir auch an, was die üblichen Rückgabetypen sind. Den Operator + kannst du mithilfe von += und dem Copy-Constructor erzeugen.


Anmelden zum Antworten