Indexoperatorüberladung Matrix



  • Hallo, ich schreibe mir zurzeit eine Matrixklasse und beschäftige mich unter anderem auch mit Operatorüberladung.
    Ich hätte gerne, dass der Indexoperator bei 2D-Vektoren funktioniert.
    Mein bisheriger Code:
    Matrix.h

    #include<vector>
    #include<iostream>
    class Matrix
    {
    public:
    	Matrix();
    	Matrix(int rows, int columns);
    	~Matrix();
    	Matrix normalise();
    	int get_rows();
    	int get_columns();
    	 std::vector<double> operator[](const int &index) ;
    
    private:
    	std::vector<std::vector<double>> m;
    	int rows;
    	int columns;
    
    };
    

    Matrix.cpp

    std::vector<double> Matrix::operator[](const int & index)
    {
    
    	return std::vector<double>(this->m[index]);
    }
    

    Vermutlich gehört die Operatorüberladung nicht in die Klasse??
    Beziehungsweise, das ist ja erst der erste Schritt, dann braucht man ja das Gleiche nochmal in ähnlicher Form.

    Ich bin für jede Hilfe dankbar
    Gruß
    C++Developer2000



  • Vermutlich gehört die Operatorüberladung nicht in die Klasse??

    Warum? Warum zwei „?“?

    Benutze std::vector<double> mit row*col Elementen. Schreibe eine Funktion

    double at(int row, int col) const
    

    die den Wert aus dem Vector liefert und eine

    double& at(int row, int col)
    

    falls du die Werte ändern willst.



  • C++Developer2000 schrieb:

    std::vector<double> Matrix::operator[](const int & index)
    {
    
    	return std::vector<double>(this->m[index]);
    }
    

    Vermutlich gehört die Operatorüberladung nicht in die Klasse??
    Beziehungsweise, das ist ja erst der erste Schritt, dann braucht man ja das Gleiche nochmal in ähnlicher Form.

    Doch. Wenn man das tatsächlich via operator[] machen möchte, dann gehört das in die Klasse.
    Aber: Da du hier eine Kopie des inneren std::vector anlegst, kannst du die Elemente in dieser
    Form lediglich auf sehr ineffiziente Weise auslesen, nicht jedoch manipulieren. So kann man also
    bestenfalls einen mäßig brauchbaren operator[](...) const implementieren.

    Um Manipulation zu ermöglichen, müsstest du eine Referenz auf den inneren std::vector zurückgeben,
    dann hast du aber das Problem, dass du einen Teil der internen (privaten) Datenstrukturen deiner
    Matrix-Klasse von außen manipulierbar machst. Ein Anweder könnte dann z.B. ein matrix[5].resize(1)
    machen, ohne dass dabei das private matrix.colums angepasst würde - nicht sehr sauber (!).

    Sowas lässt sich vermeiden, indem man keinen std::vector , sondern stattessen ein rein temporäres,
    sogenanntes Proxy-Objekt zurückgibt, das eine interne Referenz auf den inneren std::vector hält
    und nur die erlaubten Operationen ermöglicht. Bei so einer Matrix-Klasse würde so eine Proxy-Klasse
    z.B. lediglich den operator[] für die zweite Dimension (also die inneren std::vector ) zur Verfügung stellen.

    ABER: Das ist umständlich zu implementieren und zu benutzen, ohne dass es irgendwelche mir bekannten
    Vorteile bieten würde (Selbst auf englischer Tastatur tippt man nicht gerne "[][]"). Daher rate ich ebenfalls
    dringend zu der von manni66 vorgeschlagenen Lösung. Du kannst sogar statt einer .at() -Methode den
    operator() dafür verwenden, das spart Tipparbeit und der Code sieht nachher nicht so aus als hätte jemand
    einen Sack " at " darüber ausgekippt:

    mr(i, j) += m1(i, k) * m2(k, j);
    

    Es gibt einige exzellente Matrix-Bibliotheken, die u.a. das genau so implementiert haben.

    Verwende ebenfalls den von manni66 vorgeschlagenen std::vector<double> mit row*col Elementen, so
    liegen alle Elemente direkt hintereinander im Speicher, was wesentlich cache-freundlicher ist. Über die
    notwendigen Index-Berechnungen bei einem Zugriff ( index = i * cols + j ) würde ich mir keine Sorgen
    machen: Viele moderne CPUs verfügen über spezielle Instruktionen genau für solche Index-Berechnungen
    (welche der Compiler in dem Fall emittieren wird) und zwei kleine Rechenoperationen auf Registern
    sind allemal effizienter als eine Matrix-Zeile aus einer entfernten Position im Speicher in eine Cache-Line
    laden zu müssen.

    Einzige Ausnahme wären eventuell extrem große Matrizen, bei der man viel Zeilen vertauschen muss, und
    selbst da würde man möglicherweilse besser mit einer zusammenhängenden Matrix fahren, bei der man die
    Permutationen in einem separaten Vektor speichert.



  • Danke für die Antworten.
    Ich habe das jetzt folgendermaßen gelöst, werde evtl. auch noch einen 1d-Vektor anstatt einen 2d Vektor nutzen.
    Matrix. cpp

    double Matrix::operator()(int r, int c) const
    {
    
    	return this->m[r][c];
    
    }
    

    Soweit so gut, jetzt wollte ich aber auch noch den << Operator überladen.
    Dies ist mein bisheriger Ansatz:

    std::ostream & operator<<(std::ostream & output,  Matrix&  matrix)
    {
    	for (int i = 0; i < matrix.get_rows(); i++) {
    		for (int j = 0; j < matrix.get_columns(); j++) {
    
    			output << matrix(i, j) << "\t";
    		}
    		output << std::endl;
    	}
    	return output;
    }
    

    Es gibt auch keinen Compilerfehler, aber wenn ich dann eine 2 x 3 Matrix erzeuge, die nur mit Nullen gefüllt ist, wird mir folgender (wechselnder) Output angezeigt, z.B.

    02EF4CB0
    

    Die Matrix ist aber definitiv korrekt gefüllt, das hab ich verifiziert.
    Danke für jede Hilfe und allen schöne Weihnachten.

    Gruß
    C++Developer2000



  • Erstmal sollte der operator<< natürlich eine konstante Referenz auf die Matrix als Parameter nehmen, denn du willst bei der Ausgabe ja die Matrix nicht ändern.

    Außerdem: hast du kompiliert? In deinem operator<< befindet sich u.a. auch die Ausgabe des Tab-Zeichens. Das ist in deiner Ausgabe offenbar nicht enthalten. Außerdem kommen bei der Ausgabe eines Doubles keine Buchstaben raus (außer ggf. das e). Auch hast du nicht 3x2 = 6 Ausgaben. Ich schätze mal, dass der Fehler anderswo ist. Bist du dir überhaupt sicher, dass diese Ausgabe vom operator<< stammt?

    Edit: k gelöscht, danke Th69! Vermutlich wollte ich das erst irgendwie anders schreiben...



  • ftfy

    wob schrieb:

    Erstmal sollte der operator<< natürlich keine konstante Referenz auf die Matrix als Parameter nehmen, denn du willst bei der Ausgabe ja die Matrix nicht ändern.

    Wie sieht der Aufruf aus? Du wirst wahrscheinlich die Adresse ausgegeben haben...



  • Th69 schrieb:

    ftfy

    wob schrieb:

    Erstmal sollte der operator<< natürlich keine konstante Referenz auf die Matrix als Parameter nehmen, denn du willst bei der Ausgabe ja die Matrix nicht ändern.

    Wie sieht der Aufruf aus? Du wirst wahrscheinlich die Adresse ausgegeben haben...

    Genau das war der Fehler.
    Danke für die Hilfe. 🙂


Anmelden zum Antworten