Operatorüberladung (+=) und Vektoradditiion



  • Hallo zusammen, 🙂
    ich bräuchte mal einen Tipp bei folgendem Problem..
    Es geht um den Gauß-Algorithmus. Ich soll für ein vorgefertigtes Programm zwei Klassen schreiben.
    Ich konzentriere mich bei dem Code mal aufs wesentliche. Die komplette Header-Datei poste ich unten.

    try {
        z1+=z2;                                     // Dimensionen passen nicht
      }
    .....
    

    Ich muss also eine Operatorüberladung für (+= schreiben).

    Folgende Membervariablen hat die Klasse

    class Zeile {
    private:
        vector <string> zeilenElemente = {};
        int anzahlZeilenElemente;
    

    Den Rechenvorgang verstehe ich so, dass ich zu zeilenElemente den anderen Vector addieren muss und dann das Ergebnis wieder als zeilenElemente abspeichere.

    Ich habe die Methode jetzt außerhalb der Klasse als friend methode initialisiert, weil ich mir nicht sicher bin wieviele Parameter ich benötige. Das hier ist die Methode.

    void operator+=(const vector<double>& lhsVektor, const vector<double>& rhsVektor,const Zeile& zeileObjekt){	
        // Rückgabewert ist ein Array aus Integer
    
    	if(lhsVektor.size() != rhsVektor.size()){	// Kontrollieren ob Vektoren die selbe Groesse haben
    		throw string("Verschiedene Dimensionen!");
    	}
    	vector<string> ergebnisVektor;	// ErgebnisVektor deklarieren
    
    	for(int i=0; i < lhsVektor.size(); i++){	
                //string rueckgabe=lhsVektor.at(i) + rhsVektor.at(i);
                string rueckgabe=lhsVektor + rhsVektor[i];
                ergebnisVektor.push_back(rueckgabe);	// Elemente addieren 
    	}
    zeileObjekt.zeilenElemente=ergebnisVektor;	
    
    }
    

    Wenn ich drei Parameter mitgebe bekomme ich die Fehlermeldung

    Zeile.h:72:113: error: 'void operator+=(const std::vector<double>&, const std::vector<double>&, const Zeile&)' must take exactly two arguments

    Das kann ich nachvollziehen weil

    z1+=z2;
    

    ja auch nur 2 Parameter sind.
    Aber wie kann ich dann die Member Variablen ändern? 😕
    Ich müsste irgendwie den Vector [i]zeilenElemente* als Referenz übergeben können aber das funktioniert irgendwie nicht richtig 😞
    Brauche ich für diese Vektoraddition einen eigenen Konstruktor?

    Vielen Dank schonmal 🙂

    #ifndef ZEILE_H
    #define ZEILE_H
    
    class Zeile {
    private:
        vector <string> zeilenElemente = {};
        int anzahlZeilenElemente;
    
    public:
    
        //Konstruktoren
    
        Zeile() :
        zeilenElemente(),
        anzahlZeilenElemente() {
    
        }
    
        Zeile(int anzahlZeilenElementeVar) :
        zeilenElemente(0),
        anzahlZeilenElemente(anzahlZeilenElementeVar) {
    
        }
    
    //    Zeile(vector<string> lhsVektor,vector<string> rhsVektor) :
    //    zeilenElemente(lhsVektor),
    //   
    //    {}
    
        //Methodendeklaration
        void Einlesen(int anzahlZeilenElemente, ifstream &input);
        bool nullZeile(string Zeile);
        int zeileLeange(string Zeile);
    
        //Parameterueberladungen
    
        string& operator[](int index) {
            if (index > anzahlZeilenElemente - 1)
                throw string("Bereichsueberschreitung");
            else
                return zeilenElemente[index];
        }
    
    friend void operator+=(const vector<double>& lhsVektor, const vector<double>& rhsVektor,const Zeile& zeileObjekt);
    
    };
    
    void Zeile::Einlesen(int anzahlZeilenElementeVar, ifstream &input) {
    
        string zeile;
        vector<string> zeilenVektor;
    
        getline(input, zeile);
        //Einzelne Nummern auf Zeile auslesen
        istringstream s2(zeile);
        string tmp;
        for (int i = 0; i < anzahlZeilenElementeVar; i++) {
            while (s2 >> tmp) {
                if (!s2.eof()) zeilenVektor.push_back(tmp);
            }
        }
        this ->zeilenElemente = zeilenVektor;
        this ->anzahlZeilenElemente = anzahlZeilenElementeVar;
        //Test Ausgabe
        for (vector<string>::const_iterator i = zeilenVektor.begin(); i != zeilenVektor.end(); ++i)
            cout << *i;
    }
    
    //    bool Zeile::nullZeile(string zeilenElemente) {
    //        for (int i = 0; i < zeilenElemente.length(); i++) {
    //            if (stod(Zeile[i]) <= numeric_limits<double>::lowest())
    //                return false;
    //        }
    //        return true;
    //    }
    
    int Zeile::zeileLeange(string Zeile) {
        return Zeile.length();
    }
    
    void operator+=(const vector<double>& lhsVektor, const vector<double>& rhsVektor,const Zeile& zeileObjekt){	
        // Rückgabewert ist ein Array aus Integer
    
    	if(lhsVektor.size() != rhsVektor.size()){	// Kontrollieren ob Vektoren die selbe Groesse haben
    		throw string("Verschiedene Dimensionen!");
    	}
    	vector<string> ergebnisVektor;	// ErgebnisVektor deklarieren
    
    	for(int i=0; i < lhsVektor.size(); i++){	
                //string rueckgabe=lhsVektor.at(i) + rhsVektor.at(i);
                string rueckgabe=lhsVektor[i] + rhsVektor[i];
                ergebnisVektor.push_back(rueckgabe);	// Elemente addieren 
    	}
    zeileObjekt.zeilenElemente=ergebnisVektor;	
    
    }
    
    #endif /* ZEILE_H */
    


  • Wie kommst du auf "vector<double>" als Parameter für +=?
    Wenn z1 und z2 beide vom Typ Zeile sind, benötigst du

    friend void operator+=(Zeile& lhs, const Zeile& rhs);
    

    Aber wieso hast du "vector<string>" als Element der Klasse Zeile (anstatt "vector<double>"), wenn du den Gauß-Algorithmus umsetzen möchtest?



  • Th69 schrieb:

    Wie kommst du auf "vector<double>" als Parameter für +=?
    Wenn z1 und z2 beide vom Typ Zeile sind, benötigst du
    C++:
    friend void operator+=(Zeile& lhs, const Zeile& rhs);

    Das hatte ich auch schon probiert. Es kam mir nur so vor als ob die Referenz auf die Vektoren nicht richtig funktioniert weil ich dann (unter anderem) folgende Fehlermeldungen kriege.

    Zeile.h:113:15: error: 'class Zeile' has no member named 'size'
      if(lhsVektor.size() != rhsVektor.size()){ // Kontrollieren ob Vektoren die selbe Groesse haben
                   ^~~~
    Zeile.h:113:35: error: 'const class Zeile' has no member named 'size'
      if(lhsVektor.size() != rhsVektor.size()){ // Kontrollieren ob Vektoren die selbe Groesse haben
                                       ^~~~
    Zeile.h:118:29: error: 'class Zeile' has no member named 'size'
      for(int i=0; i < lhsVektor.size(); i++){
    

    Th69 schrieb:

    Aber wieso hast du "vector<string>" als Element der Klasse Zeile (anstatt "vector<double>"), wenn du den Gauß-Algorithmus umsetzen möchtest?

    Ich lese die Koeffizienten aus einer Datei aus, daher kam das. Aber stimmt das muss ich ändern 🙂



  • Du meinst dann wohl

    lhs.zeilenElemente.size()
    

    ?
    Dir scheinen die Grundlagen der Sprache nicht klar zu sein...



  • ach gott 🙄
    ich hatte ein Brett vorm Kopf. Jetzt verstehe ich es 😃



  • Ich schreibe einfach mal hier weiter damit ich keinen neuen Thread aufmachen muss.
    Es hakt schon wieder an einer Stelle 😞

    try {
        Matrix A(4,5,f_in);
        cout << "A: " << A.gib_m() << "x" << A.gib_n() << endl;
        cout << A << endl;
        Matrix StufeA(A.reduzierteStufenform());
    

    Matrix StufeA(A.reduzierteStufenform())

    Bis zu der fettgedruckten Zeile kriege ich alles hin. Die Letzte Zeile beendet das Programm mit folgender Fehlermeldung

    terminate called after throwing an instance of 'std::bad_alloc'
    what(): std::bad_alloc

    Die Methode reduzierteStufenform soll eine reduzierte Matrix "zurückliefern". Ich wusste nicht genau was mit zurückliefern gemeint ist, ich denke aber mittels return und nicht dadurch das man die Eigenschaft der Klasse ändert.
    Woran könnte dieser Fehler liegen? Ich habe gelesen dass dieser Fehler daher kommen kann dass man zu viel Speicher z.B. durch Pointer reserviert.

    Die einzige Stelle die ich sehe wo Pointer vorkommen ist der 2D Vektor. Ich weiß aber nicht wie ich ihn sonst deklarieren sollte 😕 😕

    Das ist jedenfalls mein Konstruktor und die Methode.

    Ich bin für jeden Tipp dankbar! 🙂

    Matrix (vector< vector<double> > matrix):
        matrixVektor(zeilenDim, vector<double>(spaltenDim)) {}
    
    vector< vector<double> > Matrix::reduzierteStufenform() const{
    //void Matrix::reduzierteStufenform()  {   
        vector< vector<double> > matrixReduz = matrixVektor;
        int n = matrixReduz.size();
    
        for (int i=0; i<n; i++) {
    
            //Suche nach der größten Zahl in der Spalte (Pivot Element)
            double maxEl = abs(matrixReduz[i][i]);
            int maxReihe = i;
            for (int k=i+1; k<n; k++) {
                if (abs(matrixReduz[k][i]) > maxEl) {
                    maxEl = abs(matrixReduz[k][i]);
                    maxReihe = k;
                }
            }
    
            //Tausche maximale Reihe mit der aktuellen Reihen (Spalte für Spalte)
            for (int k=i; k<n+1;k++) {
                double tmp = matrixReduz[maxReihe][k];
                matrixReduz[maxReihe][k] = matrixReduz[i][k];
                matrixReduz[i][k] = tmp;
            }
    
             //Mache alle Reihen unter dieser 0 in der aktuellen Spalte
            for (int k=i+1; k<n; k++) {
                double c = -matrixReduz[k][i]/matrixReduz[i][i];
                for (int j=i; j<n+1; j++) {
                    if (i==j) {
                        matrixReduz[k][j] = 0;
                    } else {
                        matrixReduz[k][j] += c * matrixReduz[i][j];
                    }
                }
            }
        }
        //matrixVektor =matrixReduz;
       return matrixReduz;   
    }
    

  • Mod

    //Tausche maximale Reihe mit der aktuellen Reihen (Spalte für Spalte)
            for (int k=i; k<n+1;k++) {
                double tmp = matrixReduz[maxReihe][k];
                matrixReduz[maxReihe][k] = matrixReduz[i][k];
                matrixReduz[i][k] = tmp;
            }
    

    Wieso n+1 ?



  • Ist dein Konstruktor so vollständig?

    Matrix (vector< vector<double> > matrix):
        matrixVektor(zeilenDim, vector<double>(spaltenDim)) {}
    

    Woher soll der Aufruf matrixVektor zeilenDim und spaltenDim kennen? Ich tippe mal, dass deine Klasse Matrix Membervariablen zeilenDim und spaltenDim hat, die du aber zu dem Zeitpunkt des Aufrufes noch nicht initialisiert hast.



  • Ich musste nocheinmal alles überarbeiten 🙄
    Der Typ des 2D vektors soll nicht double, sondern Zeile sein.
    In der Klasse Zeile lese ich die Zeilen ein und speichere sie als Membervariable (double Vektor).

    Ich will euch nicht den gesamten Code vor die Füße klatschen, deswegen beschränke ich mich aufs wesentliche. Wenn es nötig ist, poste ich natürlich auch alles. 😃

    class Zeile {
    private:
        //Friend-Klasse
        friend class Matrix;
        vector <double> zeilenElemente = {};
        int anzahlZeilenElemente;
    ...
    

    So, jetzt lese ich im Konstruktor die Matrix ein.
    Aufgabenstellung:

    "Im Konstruktor werden Zeilendimension sowie Spaltendimension und Datei übergeben, aus welcher die Matrixeinträge gelesen werden"

    Ich verstehe es so, dass ich zum Einlesen keine eigene Methode erstellen muss sondern das alles im Konstruktor ablaufen soll.
    Geht das vieleicht auch schöner?
    Also so dass ich den Code in die .cpp schreiben kann? 😕

    Mein eigentliches Problem ist folgendes:
    Ich muss jetzt also einen Vektor aus Objekten der Klasse Zeile erstellen. Das funktioniert aber leider nicht wegen push_back 😡 (denke ich)

    Matrix.h:53:43: error: no matching function for call to 'std::vector<std::vector<Zeile> >::push_back(Zeile&)'

    Ich hab gelesen, dass ich für push_Back einen Kopierkonstruktor brauche. Den habe ich auch geschreiben.

    Zeile(const Zeile& x) :
        zeilenElemente(x.zeilenElemente),
        anzahlZeilenElemente(x.anzahlZeilenElemente) {
        }
    

    Aber es will einfach nicht funktionieren. Ich habe die Lösung mit Pointern auch schon probiert aber alles ohne Erfolg 😞 😞

    //Konstruktor
        Matrix(int _zeilenDim, int _spaltenDim, ifstream &f_in) :
        zeilenDim(_zeilenDim),
        spaltenDim(_spaltenDim),
        matrixVektor(_zeilenDim, vector<Zeile>(_spaltenDim)) {
    
            //2d Vektor deklarieren
            vector< vector<Zeile> > tempMatrix;
    
            //zwei zeile Überspringen
            string getlineDummy;
            bool abgefragt = true;
            if (abgefragt) {
                for (size_t i = 0; i < 2; i++) getline(f_in, getlineDummy);
                abgefragt = false;
            }
    
            // 2d Vektor füllen
            for (int i = 0; i < zeilenDim; i++) {
    
              Zeile zeileObjekt;
    
              zeileObjekt.Einlesen(5,f_in);
              tempMatrix.push_back(zeileObjekt);
    
            }
    
            //this ->matrixVektor = tempMatrix;
        }
    


  • Philipp2706 schrieb:

    So, jetzt lese ich im Konstruktor die Matrix ein.
    Aufgabenstellung:

    "Im Konstruktor werden Zeilendimension sowie Spaltendimension und Datei übergeben, aus welcher die Matrixeinträge gelesen werden"

    Ich verstehe es so, dass ich zum Einlesen keine eigene Methode erstellen muss sondern das alles im Konstruktor ablaufen soll.
    Geht das vieleicht auch schöner?
    Also so dass ich den Code in die .cpp schreiben kann? 😕

    Da bin ich jetzt confused. Natürlich kannst du Konstruktor-Code in die cpp-Datei schreiben, da gehört er nämlich normalerweise auch hin.

    Philipp2706 schrieb:

    Matrix.h:53:43: error: no matching function for call to 'std::vector<std::vector<Zeile> >::push_back(Zeile&)'

    du willst einen std::vector<zeile>, nicht einen std::vector<std::vector<zeile>>. Das wäre dann nämlich schon dreidimensional.

    Philipp2706 schrieb:

    Ich hab gelesen, dass ich für push_Back einen Kopierkonstruktor brauche. Den habe ich auch geschreiben.

    Zeile(const Zeile& x) :
        zeilenElemente(x.zeilenElemente),
        anzahlZeilenElemente(x.anzahlZeilenElemente) {
        }
    

    Aber es will einfach nicht funktionieren. Ich habe die Lösung mit Pointern auch schon probiert aber alles ohne Erfolg 😞 😞

    Da wäre jetzt die Definition von "zeile" wichtig. Wenn das Ding nur aus einem std::vector<double> besteht, ist der automatisch generierte Kopierkonstruktor vollkommen ausreichend.
    Btw. wenn du std::vector benutzt, musst du die Anzahl der Elemente nicht zusätzlich noch speichern.



  • daddy_felix schrieb:

    Da bin ich jetzt confused. Natürlich kannst du Konstruktor-Code in die cpp-Datei schreiben, da gehört er nämlich normalerweise auch hin.

    okay das würde dann schon etwas ordentlicher aussehen 🙂
    Muss der ganze Konstruktor in die .cpp oder kann/soll man den Konstruktor irgendwie auftrennen?
    als Beispiel:

    Zeile() :
        zeilenElemente(), //Initialisierungsliste in die Header datei
        anzahlZeilenElemente() {
    //Dieser Bereich in die .cpp
        }
    

    daddy_felix schrieb:

    du willst einen std::vector<zeile>, nicht einen std::vector<std::vector<zeile>>. Das wäre dann nämlich schon dreidimensional.

    danke!! 👍 🙂

    daddy_felix schrieb:

    Btw. wenn du std::vector benutzt, musst du die Anzahl der Elemente nicht zusätzlich noch speichern.

    Im Aufgabentext steht, dass das Objekt die Länge der Zeile zur Verfügung stellen soll, also muss ich diese Länge als Membervariable speichern richtig?



  • Den Konstruktor nicht aufteilen. Wird dir der Kompiler auch sagen. Wie jede Funktion, Deklaration im Header, Implementation in der Cpp.

    Wenn deine Klasse 'Zeile' die Anzahl der Elemente zur Verfügung stellen soll, kann das entweder über eine Membervariable erfolgen, oder durch eine Funktion, die die Vektorgröße zurück gibt. 😉



  • Schlangenmensch schrieb:

    Den Konstruktor nicht aufteilen. Wird dir der Kompiler auch sagen. Wie jede Funktion, Deklaration im Header, Implementation in der Cpp.

    Okay..gut zu wissen 🙂

    Es folgt auch direkt das nächste Problem 😃 Ich tue mich noch echt schwer mit Klassen.

    Ich zerbreche mir gerade den Kopf darüber wie ich den Vector <Zeile> mit den darin enthaltenen Vektoren <Double> ausgeben kann.
    Ich habe es mit einem Umweg versucht, der die einzelnen Zeilen, mit Hilfe von zwei Getter-Methoden, aus der Klasse Zeile in die Operatorüberladungsmethode überträgt.

    //Getter Methode in Klasse Zeile
        vector <double> gib_zeilenElemente() const {
            return (zeilenElemente);
        }
    
    //Getter Methode in Klasse Matrix
        vector <double> gib_zeilenElemente() const {
            Zeile zeileObjekt;
            return (zeileObjekt.gib_zeilenElemente());
        }
    

    Allerdings kann hier nicht auf ein Element des Rückgabewertes der Getter-Methode zugegriffen werden.
    Netbeans gibt keine Fehlermeldung.

    ////Operatorüberladung  
    ostream &operator<<(ostream &ostr, const Matrix& matrixElement) {
        for ( size_t i = 0; i < matrixElement.zeilenDim; ++i ) {
           vector <double> ostrVektor =matrixElement.gib_zeilenElemente();  
           for (int j = 0; j < matrixElement.spaltenDim; ++j) {    
           ostr << "        " << ostrVektor.at(j);
            }
            cout << endl;
        }
     return ostr;
    }
    

    Das steht in der Main:

    try {
        Matrix A(4,5,f_in);
        cout << "A: " << A.gib_m() << "x" << A.gib_n() << endl;
        cout << A << endl;
    ...
    

    Gibt es auch eine Möglichkeit das 2D Array direkt auszulesen?
    Kann mein Ansatz überhaupt funktionieren??

    Danke:)

    Achja

    Schlangenmensch schrieb:

    Wenn deine Klasse 'Zeile' die Anzahl der Elemente zur Verfügung stellen soll, kann das entweder über eine Membervariable erfolgen, oder durch eine Funktion, die die Vektorgröße zurück gibt. 😉

    Welche dieser Varianten ist denn die "schönere"?



  • Haha ich hab es hingekriegt 🙂 🙂

    Ich habe für beide Klassen den Operator [] überladen und das hier in die verschachtelten for Schleifen geschrieben:

    ostr << "        " <<matrixElement[i].gib_zeilenElemente()[j];
    

    Ich habe noch eine Frage..

    const Zeile& Matrix::operator[](int index) const{
    

    So sieht meine Operatorüberladung aus. Kann man die const so schreiben.
    Das sieht etwas seltsam aus vorne und hinten const zu schreiben finde ich.



  • Philipp2706 schrieb:

    const Zeile& Matrix::operator[](int index) const{
    

    So sieht meine Operatorüberladung aus. Kann man die const so schreiben.
    Das sieht etwas seltsam aus vorne und hinten const zu schreiben finde ich.

    Sieht nicht seltsam aus, sondern das muss so.

    Hinten: die Funktion ändert das Matrix-Objekt selbst nicht. Wenn das const hinten nicht da wäre, könntest du den Operator nicht bei konstanten Matrix-Objekten benutzen.
    Vorn: der Rückgabewert ist eine Referenz auf einen konstanten Wert (wenn das const vorn nicht da wäre, könnte man ja non-const Methoden der Zeile aufrufen, die wiederum das Matrix-Objekt ändern würden. Dies widerspräche dem hinteren const).

    Vielleicht möchtest du den Operator auch noch non-const überladen, sodass du die Zeile auch ändern kannst. Dann 2x das const weg.



  • In diesem Zusammenhang ist bei mir noch ein Fehler aufgetreten.
    Ich würde gerne aus dem <Zeile> Vektor ein 2D double Array machen.

    Ich kann den double Wert aber einfach nicht im Array speichern 😮 😕

    In einer double Variable geht es schon.

    Ich habe mir gedacht dass es mit dem const in der Operatorüberladung zusammenhängen kann. Deswegen habe ich den Operator noch zusätzlich ohne const überladen aber es klappt immer noch nicht.

    Irgendjemand eine Idee woran das liegen könnte?

    Ich hab das mal ganz kleinschrittig gemacht. Die Zeile " matrixDouble[i][j]=a;" macht Probleme.

    for (int i = 1; i < 3; ++i) {
            for (int j = 1; j < 3; ++j){
                double a=matrixElement[i].gib_zeilenElemente()[j];
             matrixDouble[i][j]=a;
                cout << a;
            }    }
    

    Edit: Hat sich erledigt. Ich habe einfach ein Array statt eines Vektors genommen. So funktioniert es, warum auch immer 😃


Anmelden zum Antworten