Entwicklung einer einfachen Matrizenklasse



  • Danke, damit werde ich ein wenig beschäftigt sein.

    @wob sagte in Entwicklung einer einfachen Matrizenklasse:

    • Du könntest eine private Funktion zur Berechnung des Index machen. Field2d::row_col_to_index oder so. Ich vergesse nämlich gerne, ob ich row-major oder col-major bin.

    Das verstehe ich glaub ich, nicht ganz. Du meinst, falls jemand eingibt (column, row), das dies automatisch zu (row, column) wird? Aber woher soll ich das wissen, ob jemand column meint und nicht row?

    @Swordfish sagte in Entwicklung einer einfachen Matrizenklasse:

    @zeropage sagte in Entwicklung einer einfachen Matrizenklasse:

      elements_.resize(rows_ * columns_);
    

    Warum nicht den ctor von vector nehmen?

    std::vector<int> elements_(rows * columns);?
    Und wie greife ich von dort mit element() zu?

    @Swordfish sagte in Entwicklung einer einfachen Matrizenklasse:

    Warum sind die Parameter const?

    Vor längerer Zeit hatte ich ein Stilfrage gestellt, ob es ok ist, alle Variablen, die sich nicht mehr ändern werden, auch die lokalsten constzu machen. Dies wurde bejaht und jemand schrieb, das er es auch so mache, nur bei den Parametern vergisst er es manchmal. Und ich dachte mir, warum nicht auch bei den Parametern, wenn sie sich nicht mehr ändern.

    Warum ist der Elementtyp kein Templateparameter?

    Weil wir noch bei 0 sind. Hatte ich mir aber auch überlegt, bin aber der Meinung, das kann gemacht werden, wenn die Rechenoperationen drin sind.



  • @zeropage sagte in Entwicklung einer einfachen Matrizenklasse:

    Das verstehe ich glaub ich, nicht ganz. Du meinst, falls jemand eingibt (column, row), das dies automatisch zu (row, column) wird?

    Nein.

    Ich meine, dass du bislang schon mehrfach const std::size_t idx = columns_ * row + column; geschrieben hast. Wenn du in alles weiteren Funktionen, die du noch so einbauen wirst, die Berechnung selbt machen willst, bitte. Wundere dich dann aber nicht, wenn du mal irgendwann const std::size_t idx = rows_ * column + row; schreibst (das ginge ja auch).

    Warum nicht den ctor von vector nehmen?

    std::vector<int> elements_(rows * columns);?
    Und wie greife ich von dort mit element() zu?

    Hä? Verstehe den Zusammenhang nicht.



  • @wob sagte in Entwicklung einer einfachen Matrizenklasse:

    Hä? Verstehe den Zusammenhang nicht.

    Die Frage war an Swordfish gerichtet. Wenn ich einen vector mit Konstruktor aufrufe, wird er ja dort erstellt. Wie bringe ich ihn dann in den Memberbereich? Ich wüßte jetzt nur

    std::vector<int> elements(rows * columns);
    elements_ = elements;
    

    Ansonsten danke nochmals. Hatte mir in der Zwischenzeit einige Gedanken zu den anderen Vorschlägen gemacht. Werde also etwas beschäftigt sein. Bis später oder so.



  • @zeropage sagte in Entwicklung einer einfachen Matrizenklasse:

    Die Frage war an Swordfish gerichtet.

    Initialisierungsliste

    #include <vector>
    
    template<typename T>
    class mat2d_t
    {
    public:
    	using value_type = T;
    	using reference = T&;
    	using const_reference = T const&;
    	using size_type = typename std::vector<T>::size_type;
    
    private:
    	size_type num_cols = 0;
    	std::vector<T> data;
    
    public:
    	mat2d_t() = default;
    	
    	mat2d_t(size_type rows, size_type cols, const_reference value = value_type{})
    	: num_cols { cols },
    	  data     (rows * cols, value)
    	{}
    };
    


  • Super, danke schön. Werde ich berücksichtigen.



  • wob meint:

    #include <vector>
    
    template<typename T>
    class mat2d_t
    {
    public:
    	using value_type = T;
    	using reference = T&;
    	using const_reference = T const&;
    	using size_type = typename std::vector<T>::size_type;
    
    private:
    	size_type num_cols = 0;
    	std::vector<T> data;
    
    public:
    	mat2d_t() = default;
    	
    	mat2d_t(size_type rows, size_type cols, const_reference value = value_type{})
    	: num_cols { cols },
    	  data     (rows * cols, value)
    	{}
    
    	// das da:
    private:
    	size_type to_linear_index(size_type row, size_type col) const
    	{
    		return num_cols * row + col;
    	}
    };
    


  • Kleiner Zwischenstand mit gestrichenen Segeln. Die Syntax f[x][y]mit Stichwort Proxy-Klasse kapiere ich einfach nicht. Mehr kann ich jetzt echt nicht mitteilen.
    Außer das da wahrscheinlich noch weitere Baustellen sind.
    Und, das ich mich für die Zeilen- und Spaltenanzahl auf std::size_t festlegen möchte. "einfache Matrizenklasse" soll wortwörtlich gelten.

    template<typename T>
    class Field2D
    {
    public:
    	using valType = T;
    	using valVec = std::vector<valType>;
    
    	Field2D() = default;
    	Field2D(const std::size_t rows, const std::size_t columns)
    		: columns_(columns), elements_(rows * columns) {}
    
    	std::size_t rows() const
    	{
    		return elements_.size() / columns_; //kann doch nicht sein???
    	}
    	std::size_t columns() const
    	{
    		return columns_;
    	}
    
    	valType element(const std::size_t row, const std::size_t column) const
    	{
    		const std::size_t idx = toLinearIndex(row, column);
    		return elements_[idx];
    	}
    
    	valType operator()(const std::size_t row, const std::size_t column) const
    	{
    		const std::size_t idx = toLinearIndex(row, column);
    		return elements_[idx];
    	}	
    
    	void setElement(const std::size_t row, const std::size_t column, const valType& v)
    	{
    		const std::size_t idx = toLinearIndex(row, column);
    		elements_[idx] = v;
    	}
    
    private:
    	std::size_t columns_ = 0;
    	valVec elements_;
    
    	std::size_t toLinearIndex(const std::size_t row, const std::size_t column) const
    	{
    		if (row >= rows())
    			throw std::out_of_range("toLinearIndex(): row: " + std::to_string(row));
    		if (column >= columns())
    			throw std::out_of_range("toLinearIndex(): column: " + std::to_string(column));
    
    		return columns() * row + column;
    	}
    };
    


  • @zeropage sagte in Entwicklung einer einfachen Matrizenklasse:

    return elements_.size() / columns_; //kann doch nicht sein???
    

    Warum kann das nicht sein?



  • Weil ne extra Division für eine simple Rückgabe? Macht das nichts aus?



  • Wie oft brauchst Du das denn? Sonst musst Du es Dir eben merken.

    @zeropage sagte in Entwicklung einer einfachen Matrizenklasse:

    Die Syntax f[x][y] mit Stichwort Proxy-Klasse kapiere ich einfach nicht.

    So zB:

    #include <iostream>
    #include <vector>
    
    template<typename T>
    class mat2d_t
    {
    public:
    	using value_type = T;
    	using reference_type = T&;
    	using size_type = typename std::vector<T>::size_type;
    
    	class row_t
    	{
    		T* row_data;
    		size_type num_cols;  // fuer evtl range-check
    
    	public:
    		row_t(T *row_data, size_type num_cols)
    		: row_data{ row_data }, num_cols{ num_cols }
    		{}
    	
    		T& operator[](size_type col) { return row_data[col]; }
    	};
    
    private:
    	size_type num_cols = 0;
    	std::vector<T> data;
    
    public:
    	mat2d_t() = default;
    	
    	mat2d_t(size_type rows, size_type cols, value_type const &value = value_type{})
    	: num_cols { cols },
    	  data     ( rows * cols, value )
    	{}
    
    	row_t operator[](size_type row) {
    		return { &data[row * num_cols], num_cols };
    	}
    };
    
    int main()
    {
    	mat2d_t<int> mat2d(2, 5, 42);
    	for (std::size_t y{}, i{ 1 }; y < 2; ++y)
    		for (std::size_t x{}; x < 5; ++x, ++i)
    			mat2d[y][x] = i;
    
    	for (std::size_t y{}, i{ 1 }; y < 2; ++y, std::cout.put('\n'))
    		for (std::size_t x{}; x < 5; ++x, ++i, std::cout.put(' '))
    			std::cout << mat2d[y][x];
    }
    


  • Besten Dank mal wieder.

    Mache jetzt aber Feierabend , guts Nächtle... 😉





  • Jupp.



  • @zeropage sagte in Entwicklung einer einfachen Matrizenklasse:

    Und, das ich mich für die Zeilen- und Spaltenanzahl auf std::size_t festlegen möchte. "einfache Matrizenklasse" soll wortwörtlich gelten.

    Das Template dient doch aber dazu, dass die int, double oder sonstwas in deiner Matrix speichern kannst - irgendwas, mit dem man rechnen kann wie zum Beispiel big integers.

    Oder warum kommst du auf das size_t? Andererseits wird es vom vector ja schon automatisch vorgegeben.



  • Die Typ der Elemente der Matrix sollen natürlich möglichst beliebig sein, also alles mit was man rechnen kann.

    Ich meinte jetzt std::size_t columns_. Weil ich mir einbilde, dafür auch schon mal ein Template gesehen zu haben. Kann mich aber voll irren, alleine weil ich mir etwas anderes als size_tgar nicht vorstellen kann.



  • @zeropage sagte in Entwicklung einer einfachen Matrizenklasse:

    Weil ich mir einbilde, dafür auch schon mal ein Template gesehen zu haben.

    Du meinst einen Template-Typ-Parameter für den Typ der Größenangaben?



  • Richtig. Können aber auch Gespenster in meinem Kopf sein.





  • @DocShoe Den Thread habe ich gesucht, aber nicht mehr gefunden 👍



  • Ein Mini-Zwischenstand. Eigentlich dachte ich mir, mit der Proxy-Klasse wäre dieses Kapitel abgeschlossen, aber bin mir nicht mehr sicher. Ist das soweit in Ordnung?

    template<typename T>
    class Mat2D_T
    {
    public:
    	using valType = T;
    	using size_t = std::size_t;
    
    	class Row
    	{
    		valType* row_data;
    		size_t cols_ = 0;  
    
    	public:
    		Row(valType* row_data, const size_t columns)
    			: row_data{ row_data }, cols_{ columns } {}
    
    		valType& operator[](const size_t column)
    		{
    			if (column >= cols_)
    				throw std::out_of_range("operator[](): column: " + std::to_string(column));
    
    			return row_data[column];
    		}
    	};
    	
    	Mat2D_T() = default;
    	Mat2D_T(const size_t rows, const size_t columns)
    		: columns_{ columns }, elements_(rows * columns) {}
    
    	size_t rows() const
    	{
    		return elements_.size() / columns_; //kann man sich die Länge nicht von der Klasse Row geben lassen?
    	}
    
    	size_t columns() const
    	{
    		return columns_;
    	}
    
    	Row operator[](const size_t row)
    	{
    		if (row >= rows())
    			throw std::out_of_range("operator[](): row: " + std::to_string(row));
    
    		return { &elements_[row * columns_], columns_ };
    	}
    
    	/*diese Funktionen werde ich später doch bestimmt auch noch benötigen?*/
    	valType element(const size_t row, const size_t column) const
    	{
    		const size_t idx = toLinearIndex(row, column);
    		return elements_[idx];
    	}
    
    	valType operator()(const size_t row, const size_t column) const
    	{
    		const size_t idx = toLinearIndex(row, column);
    		return elements_[idx];
    	}
    
    	void setElement(const size_t row, const size_t column, const valType& v)
    	{
    		const size_t idx = toLinearIndex(row, column);
    		elements_[idx] = v;
    	}
    
    private:
    	size_t toLinearIndex(const size_t row, const size_t column) const
    	{
    		if (row >= rows())
    			throw std::out_of_range("toLinearIndex(): row: " + std::to_string(row));
    		if (column >= columns())
    			throw std::out_of_range("toLinearIndex(): column: " + std::to_string(column));
    
    		return columns() * row + column;
    	}
    	/////////////////////////////////////////////////////////////////////////
    
    	size_t columns_ = 0;
    	std::vector<valType> elements_;
    };
    
    

Anmelden zum Antworten