einfache Matrizenklasse, Vererbung
-
@Swordfish Sorry, dachte es geht so, den
std::back_inserter
braucht es anscheinend.std::copy(f0.begin(), f0.end(), std::back_inserter(vArray))
-
Bei Übergabe eines Vektor muss ich noch die Dimensionen angeben, also
Mat mt(3, 4, vec);
. Das entfällt auch bei einem Doppelvektor. Wie gesagt, als Eingabe, nicht als Struktur.
-
Die neue Array-Klasse ist jetzt soweit fertig, waren nur einige Inhalte der .cpp auszutauschen. Ich weiß, Ihr schreibt sowas in drei Minuten
using floatVec = std::vector<float>; using floatArray = std::vector<std::vector<float>>; //Ja, die dummen Namen sind noch da :roll_eyes: class Matrix { protected: Matrix() {} ~Matrix() {} Matrix(const Matrix& mx) {} Matrix operator=(const Matrix& mx) { copyMatrix(mx); } Matrix(const std::size_t rows, const std::size_t columns); Matrix(const std::size_t rows, const std::size_t columns, const float v); Matrix(const std::size_t rows, const std::size_t columns, const floatVec& vec); Matrix(const floatArray& array); public: float element(const std::size_t row, const std::size_t column) const; floatVec elements() const; std::size_t rows() const; std::size_t columns() const; std::size_t size() const; floatVec row(const std::size_t r) const; floatVec column(const std::size_t c) const; void printElement(const std::size_t row, const std::size_t column, std::ostream& stream = std::cout) const; void printRow(const std::size_t row, std::ostream& stream = std::cout) const; void printColumn(const std::size_t column, std::ostream& stream = std::cout) const; void print(const std::string& note = {}, std::ostream& stream = std::cout) const; protected: void fillMatrix(const float v); //könte wegfallen, wird aber in der Testphase noch benötigt void fillMatrix(const floatVec& vec); void fillMatrix(const floatArray& array); void setElement(const std::size_t row, const std::size_t column, const float v); void copyMatrix(const Matrix& mx); bool operator == (const float v) const; bool operator != (const float v) const; bool sameDimension(const Matrix& mx) const; bool operator == (const Matrix& mx) const; bool operator != (const Matrix& mx) const; private: std::size_t rows_ = 0; std::size_t columns_ = 0; floatVec elements_; float val_ = 0.; void resizeElements(const float v); floatArray toArray(const floatVec& vec) const; /*ist noch drin, weil ich die Funktion einen DoppelVector!=(rows * columns) aufnehmen zu können, für einzelne Vektoren nicht ändern wollte*/ };
-
Wieso verletzt du die Rule of zero? Was denkst du, wieso du protected-Attribute brauchst? Was passiert bei einem Vergleich einer Matrix mit einem Skalar?
-
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.