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 innerenstd::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 brauchbarenoperator[](...) 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. einmatrix[5].resize(1)
machen, ohne dass dabei das privatematrix.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 innerenstd::vector
hält
und nur die erlaubten Operationen ermöglicht. Bei so einer Matrix-Klasse würde so eine Proxy-Klasse
z.B. lediglich denoperator[]
für die zweite Dimension (also die innerenstd::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>
mitrow*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. cppdouble 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.