Entwicklung einer einfachen Matrizenklasse



  • @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_;
    };
    
    


  • Eine Sache richtig zu machen ist um Welten schwerer, als sie funktionierend zu machen.

    Ich wollte die Methoden in eine .cpp auslagern, um mehr Platz im Header zu haben und bekomme lauter unresolved externals 😕



  • @zeropage Du kannst Templates nicht so einfach in eine Cpp auslagern.


Anmelden zum Antworten