einfache Matrizenklasse, Vererbung
-
Weil ich mich an die Rule of Three halten wollte.
Weil ich die Funktionen public machen wollte, aber nicht private machen konnte.Nicht eingebaut, wußte gar nicht, das man das brauchen könnte. Wie gesagt, ist ne einfache Klasse.
-
@zeropage sagte in einfache Matrizenklasse, Vererbung:
Weil ich mich an die Rule of Three halten wollte.
Aber wieso? Du verwaltest keine Ressourcen, was so auch richtig ist. Hingegen sind deine 3 falsch implementiert.
Weil ich die Funktionen public machen wollte, aber nicht private machen konnte.
Also mach sie public?
Nicht eingebaut, wußte gar nicht, das man das brauchen könnte. Wie gesagt, ist ne einfache Klasse.
Na, du sagst in Zeile 39/40, dass deine Klasse das könnte. Ich wundere mich hingegen, was das sein soll, weil das nicht sinnvoll klingt. Also lügt deine Zeile 39/40?
PS: Wozu ist der Member
val_
da?
-
@SeppJ sagte in einfache Matrizenklasse, Vererbung:
Aber wieso? Du verwaltest keine Ressourcen, was so auch richtig ist. Hingegen sind deine 3 falsch implementiert.
Wie würde man das richtig machen? Und ich brauche keinen Destruktor und Kopier-Konstruktor?
@SeppJ sagte in einfache Matrizenklasse, Vererbung:
Also mach sie public?
Dann passiert das, was Bashar mit
diag.resizeMatrix(3, 4);
meinte. Auch wenn die jetzt weg ist.
Edit: Hatte mich verschrieben, meinte nicht public machen wollte.@SeppJ sagte in einfache Matrizenklasse, Vererbung:
Na, du sagst in Zeile 39/40, dass deine Klasse das könnte. Ich wundere mich hingegen, was das sein soll, weil das nicht sinnvoll klingt. Also lügt deine Zeile 39/40?
Da sollen eigentlich floats auf Gleicheit geprüft werden. Aber ich müsste natürlich noch den Ort des float angeben. Danke.
@SeppJ sagte in einfache Matrizenklasse, Vererbung:
PS: Wozu ist der Member val_ da?
Ist der Wert mit dem standardmäßig eine Matrix gefüllt werden kann oder soll.
-
@zeropage sagte in einfache Matrizenklasse, Vererbung:
Wie würde man das richtig machen? Und ich brauche keinen Destruktor und Kopier-Konstruktor?
@zeropage sagte in einfache Matrizenklasse, Vererbung:
Da sollen eigentlich floats auf Gleicheit geprüft werden. Aber ich müsste natürlich noch den Ort des float angeben. Danke.
Versteh' ich nicht.
@zeropage sagte in einfache Matrizenklasse, Vererbung:
Ist der Wert mit dem standardmäßig eine Matrix gefüllt werden kann oder soll.
Einfach mit
float{}
initialisieren?
-
@Swordfish sagte in einfache Matrizenklasse, Vererbung:
@zeropage sagte in einfache Matrizenklasse, Vererbung:
Da sollen eigentlich floats auf Gleicheit geprüft werden. Aber ich müsste natürlich noch den Ort des float angeben. Danke.
Versteh' ich nicht.
Ich jetzt auch nicht mehr
Eigentlich sollte in der Funktion nur alle Werte der Matrix mit einer anderen verglichen werden. Habe ich wohl verbockt. Bis auf
sameDimension()
sind die dortigen Vergleichsoperatoren noch gar nicht implementiert.Ist natürlich blöd, unfertige Funktionen zu zeigen, aber so hätte ich gar nicht gewusst, was noch alles an der Klasse nicht richtig stimmt.
@Swordfish sagte in einfache Matrizenklasse, Vererbung:
Einfach mit float{} initialisieren?
die
val_
kommt ganz raus, ist überflüssig wie dassize_
ganz oben. Wenn ein bestimmter Wert rein soll, passiert das über den Konstruktor. Außer das ist auch nicht in Ordnung.
-
Hast du die Klasse mal ausprobiert? Wie erstellst du denn eine Instanz mit den protected Konstruktoren?
-
@zeropage sagte in einfache Matrizenklasse, Vererbung:
Eigentlich sollte in der Funktion nur alle Werte der Matrix mit einer anderen verglichen werden.
Das klingt mehr nach
find()
odercomponent_exists()
oder sowas.@zeropage sagte in einfache Matrizenklasse, Vererbung:
die
val_
kommt ganz raus, ist überflüssig wie dassize_
ganz oben.Unbedingt merken musst Du Dir nur
width
(columns
),size
merkt sich der vector.
-
@Jockelx sagte in einfache Matrizenklasse, Vererbung:
Hast du die Klasse mal ausprobiert? Wie erstellst du denn eine Instanz mit den protected Konstruktoren?
Die werden hier aufgerufen
class Mat : public Matrix { public: Mat() {} ~Mat() {} Mat(const Matrix& mt) : Matrix(mt) {} Mat operator=(const Matrix& mt) { copyMatrix(mt); } Mat(const std::size_t rows, const std::size_t columns) : Matrix(rows, columns) {} Mat(const std::size_t rows, const std::size_t columns, const float v) : Matrix(rows, columns, v) {} Mat(const std::size_t rows, const std::size_t columns, const floatVec& vec) : Matrix(rows, columns, vec) {} Mat(const floatArray& fArray) : Matrix(fArray) {} protected: /*void operator+=(const float f); void operator-=(const float f); void operator*=(const float f); void operator/=(const float f); void operator+=(const Mat& mt); void operator-=(const Mat& mt); Mat operator+(const float f) const; Mat operator-(const float f) const; Mat operator*(const float f) const; Mat operator/(const float f) const; Mat operator+(const Mat& mt) const; Mat operator-(const Mat& mt) const; Mat operator*(const Mat& mt) const;*/ }; class DMat : public Mat { public: DMat() {} ~DMat() {} DMat(const Mat& mt) : Mat(mt) {} DMat operator=(const Mat& mt) {} DMat(const std::size_t size) : Mat(size, size), unitSize_(size) { constructD(); } DMat(const std::size_t size, const float dv) : Mat(size, size), unitSize_(size), dv_(dv) { constructD(); } void setDv(const float v) { dv_ = v; constructD(); } float dv() const { return dv_; } std::size_t uSize() const { return unitSize_; } private: std::size_t unitSize_ = 0; float dv_ = 1.; void constructD() { for (std::size_t n = 0; n < unitSize_; ++n) { setElement(n, n, dv_); } } }; class RVec : public Mat { public: RVec() {} ~RVec() {} RVec(const Mat& rv) : Mat(rv) {} RVec operator=(const Mat& rv) {} RVec(const std::size_t size) : Mat(1, size), size_(size) { } RVec(std::initializer_list<float> list) : Mat(1, list.size(), list), size_(list.size()) {} RVec(const floatVec& vec) : Mat(1, vec.size(), vec), size_(vec.size()) {} RVec(const std::size_t size, const floatVec& vec) : Mat(1, size, vec), size_(size) {} private: std::size_t size_ = 0; //noch testweise drin }; /*class CVec : public Mat { public: CVec() {} ~CVec() {} CVec(const Mat& cv) : Mat(cv) {} CVec operator=(const Mat& cv) {} CVec(const std::size_t size) : Mat(size, 1), size_(size) { } CVec(std::initializer_list<float> list) : Mat(list.size(), 1, list), size_(list.size()) {} CVec(const floatVec& vec) : Mat(vec.size(), 1, vec), size_(vec.size()) {} CVec(const std::size_t size, const floatVec& vec) : Mat(size, 1, vec), size_(size) {} private: std::size_t size_ = 0; //noch testweise drin };*/
-
@Swordfish sagte in einfache Matrizenklasse, Vererbung:
Das klingt mehr nach
find()
odercomponent_exists()
oder sowas.Ja, danke für die Bezeichner. Ich hatte ohne Implementierung noch nicht richtig darüber nachgedacht, deshalb ist das bei rausgekommen.
-
@zeropage sagte in einfache Matrizenklasse, Vererbung:
Die werden hier aufgerufen
Ah, okay, das ist eine Basisklasse. Ob das jetzt sinnig ist, sei dahin gestellt, aber mach mal den Destruktor virtual, sonst sieht die Klasse so aus, als ob sie nicht so gerne möchte, dass von ihr abgeleitet wird.
-
Doppelpost
-
Ok, danke. Dachte, das wäre nur bei virtuellen Funktionen notwendig. Ist noch was mit den Standard-Konstruktoren nicht richtig?
@Jockelx sagte in einfache Matrizenklasse, Vererbung:
Ah, okay, das ist eine Basisklasse. Ob das jetzt sinnig ist, sei dahin gestellt,
Wäre es besser darauf zu verzichten und die Matrix-Operatoren auch in die Matrix-Klasse statt in die Mat zu packen?
-
@zeropage
Ja, und nein. Die Operatoren solltet du nach Möglichkeit als freie Funktionen implementieren.
-
Richtig, hatte ich auch vor, deshalb sind sie noch auskommentiert. Sie arbeiten aber korrekt. Nur mit der Vererbung habe ich Probleme. Zu dem Thema hatte auch Finnegan gestern was geschrieben.
-
@zeropage sagte in einfache Matrizenklasse, Vererbung:
Na, zum Beispiel
... std::vector<float> f3 = { .1, .2, .3 }; vArray.push_back(f3); Mat mt(vArray); mt.print("mt");
Wie würdest Du denn das machen?
Auch wenn die Diskussion gerade schon woanders angelangt ist, ich würde das so machen:
Matrix m { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } }; std::cout << m << "\n";
Damit das so funktioniert, krame ich hier mal was aus meiner eigenen Codesammlung heraus. Das kannst du gerne als Inspiration nehmen, wie so eine Klasse aussehen könnte (sorry, ist was länger):
#include <cstdint> #include <iostream> #include <algorithm> #include <vector> template <typename T> class Matrix { public: Matrix(std::size_t rows, std::size_t columns); Matrix(std::initializer_list<std::initializer_list<T>> init); auto rows() const -> std::size_t; auto columns() const -> std::size_t; auto operator()(std::size_t i, std::size_t j) const -> const T&; auto operator()(std::size_t i, std::size_t j) -> T&; private: const std::size_t rows_; const std::size_t columns_; std::vector<T> elements_; }; template <typename T> Matrix<T>::Matrix(std::size_t rows, std::size_t columns) : rows_{ rows }, columns_{ columns }, elements_( rows * columns ) { } template <typename T> Matrix<T>::Matrix(std::initializer_list<std::initializer_list<T>> init) : Matrix{ init.size(), std::max( init, [](const auto& a, const auto& b) { return a.size() < b.size(); } ).size() } { std::size_t i = 0; for (const auto& row : init) { if (i >= rows()) break; std::size_t j = 0; for (const auto& value : row) { if (j >= columns()) break; (*this)(i, j) = value; ++j; } ++i; } } template <typename T> auto Matrix<T>::rows() const -> std::size_t { return rows_; } template <typename T> auto Matrix<T>::columns() const -> std::size_t { return columns_; } template <typename T> auto Matrix<T>::operator()(std::size_t i, std::size_t j) const -> const T& { return elements_[ i * columns() + j]; } template <typename T> auto Matrix<T>::operator()(std::size_t i, std::size_t j) -> T& { return elements_[ i * columns() + j]; } template <typename T> std::ostream& operator<<(std::ostream& out, const Matrix<T>& m) { out << typeid(T).name() << m.rows() << "x" << m.columns(); out << "{ "; for (std::size_t i = 0; i < m.rows(); ++i) { if (i > 0) out << ", "; out << "{ "; for (std::size_t j = 0; j < m.columns(); ++j) { if (j > 0) out << ", "; out << m(i, j); } out << " }"; } out << " }"; return out; } auto main() -> int { Matrix<float> m { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } }; std::cout << m << std::endl; }
Ausgabe (https://ideone.com/hX3l5T):
f3x3{ { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } }
-
Aha, toll. Danke
Bezüglich der "händischen" Eingabe über die Konsole; ich denke, das Thema kann man vergessen. Wann macht das später schon in der Anwendung? Ist ja nur zu Testzwecken.
-
@zeropage sagte in einfache Matrizenklasse, Vererbung:
Nur mit der Vererbung habe ich Probleme.
Nein. Das meinst du vielleicht, aber du hast noch viel mehr Probleme. Dein Code hat momentan viel zu viele Baustellen. Außerdem habe ich Zweifel, ob hier Vererbung überhaupt eine sinnvolle Abstraktion ist. Haben Dmat, RVek, CVek oder was immer du da alles ableiten möchtest, irgendwelche relevanten Eigenschaften? Das einzige, was sie bieten, sind spezielle Konstruktoren. Das sind keine Spezialklassen, sondern nur besondere Initialisierungen. Die kannst du entweder direkt in der Matrixklasse anbieten oder als Hilfsfunktionen, die entsprechende Objekte bauen, z.B. eine Funktion für Einheitsmatrizen der Größe N, die dir einfach nur den Matrixkonstruktor passend aufruft. Faustregel: Wenn eine Klasse keine Membervariablen hat, dann ist es keine sinnvoll definierte Klasse.
Empfehlung: Nimm ganz weit Abstand und implementier erst einmal nur die
Matrix
-Klasse. Keine Spezialfälle, keine Vererbung, dafür aber vollständig, richtig, und gründlich getestet. Dann merkst du, was du überhaupt wirklich brauchst und was Planungsrelikte sind. Wenn damit zufrieden bist, fragst du uns noch einmal nach unserer Meinung zu der vollständigen Klasse.Und danach kannst du dir dann Spezialfälle vornehmen, aber ich sage voraus, dass die Basisklasse die meisten schon erschlagen wird.
-
Ich glaube, ich verstehe dein gesamtes Vorgehen nicht. Ich stelle mir die Frage, was das alles soll.
Also zum Beispiel, dass Mat von Matrix erbt. Was unterscheidet die beiden? Ist Vererbung hier überhaupt sinnvoll - sollten nicht die Operationen von Mat eher in Matrix sein?
Warum eine spezielle Klasse DMat (ich nehme mal an, dass soll für Diagonalmatrix stehen?) - die aber bei dir von Mat erbt?! Das Besondere an einer Diagonalmatrix ist doch, dass alles 0 ist außer der Diagonalen. Du brauchst also, wenn du eine Matrix der Größe hast, die diagonal ist, nur Werte zu speichern. Der Rest ist 0. Das erlaubt sehr viele "Shortscuts" bei Berechnungen. Das spricht absolut dagegen, von Matrix zu erben. Warum gibt es die Member-Variable
dv_
? Und was passiert mit ihr, wenn du ein Element der Diagonalen auf was anderes setzt?Generell scheinst du extrem viele Funktionen zu haben - konzentriere dich lieber darauf, die wirklich wichtigen Funktionen zu implementieren.
Was sind CVec und RVec?
Fragen über Fragen...
Zur Namensgebung, dass Array bei dir nicht Array, sondern 2d-Array bedeutet, hatte ich ja schon was gesagt.
-
RVec und CVec sollen Zeilen- und Spaltenvektoren sein
Aber ok. Mit irgendwas muss ich ja anfangen. Wenn Vererbung hier nicht passt, habe ich sonst keine Anwendung, in der das passen würde.
Und wenn doch was kommen würde, würde ich immer noch nicht wissen, was man da tun muss.
-
@SeppJ sagte in einfache Matrizenklasse, Vererbung:
Empfehlung: Nimm ganz weit Abstand und implementier erst einmal nur die
Matrix
-Klasse. Keine Spezialfälle, keine Vererbung, dafür aber vollständig, richtig, und gründlich getestet. Dann merkst du, was du überhaupt wirklich brauchst und was Planungsrelikte sind. Wenn damit zufrieden bist, fragst du uns noch einmal nach unserer Meinung zu der vollständigen Klasse.So betrachtet ist das eigentlich in Ordnung. Ich muss ja keine Vererbung haben, ich wollte nur. Aber stimmt, Lebenszeit ist kostbar. Sollte man nicht rumtrödeln.