Entwicklung einer einfachen Matrizenklasse





  • @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.



  • Stimmt wohl.



  • Noch der operator*() und dann ist der Grundaufbau doch eigentlich fertig?

    Mat2D_T operator*(const Mat2D_T& mT) const
    	{
    		if (columns() != mT.rows())
    			throw std::exception("operator*()");
    
    		Mat2D_T nmT(rows(), mT.columns());
    		valType v = {};
    		for (size_t c = 0; c < mT.columns(); ++c)
    		{
    			for (size_t r = 0; r < rows(); ++r)
    			{
    				v = element(r, 0) * mT.element(0, c);
    				for (size_t l = 1; l < columns(); ++l)
    				{
    					v += element(r, l) * mT.element(l, c);
    				}
    				nmT[r][c] = v;
    			}
    			v = {};
    		}
    		return nmT;
    	}
    


  • @zeropage sagte in Entwicklung einer einfachen Matrizenklasse:

      return elements_.size() / columns_; //kann man sich die Länge nicht von der Klasse Row geben lassen?
    

    Wann existiert denn immer ein Row?



  • @Swordfish Immer nur wenn operator[]()aufgerufen wird?



  • Eben. Wenn Du die anzahl der rows nicht jedes mahl ausrechnen willst dann musst Du sie Dir wo merken.



  • Alles klar 🙂



  • Frage aus Neugier:

    valType operator()(const size_t row, const size_t column) const
    	{
    		const size_t idx = toLinearIndex(row, column);
    		return elements_[idx];
    	}
    

    benötige ich eigentlich nicht mehr, mich würde aber interessieren, wie man

    Mat2D_T<int> mat(3, 4);
    mat(1, 1) = 5;
    

    realisieren kann?



  • Einfach eine Referenz zurückgeben:

    valType& operator()(size_t row, size_t column)
    {
    	const size_t idx = toLinearIndex(row, column);
    	return elements_[idx];
    }
    


  • @Th69 sagte in Entwicklung einer einfachen Matrizenklasse:

    Einfach eine Referenz zurückgeben:

    valType& operator()(size_t row, size_t column) const
    

    const?



  • Ups, habe es nur von @zeropage kopiert gehabt, aber nun korrigiert.



  • Aha, ich hätte schwören können, das ich mir dachte, das es so gehen muss. Danke 🙂



  • @zeropage sagte in Entwicklung einer einfachen Matrizenklasse:

    Frage aus Neugier:

    valType operator()(const size_t row, const size_t column) const
    

    benötige ich eigentlich nicht mehr, [...]

    mat(1, 1) = 5;
    

    Ich will dich ja nicht in den Wahnsinn treiben, jetzt wo du dir mit deiner operator[]-Proxyklasse so viel Mühe gegeben hast.

    Aber tipp mal bitte 100x mat[1][1] = 5; und 100x mat(1, 1) = 5; und sag mir danach, ob du lieber den operator[] und die komplette Proxy-Klasse oder den operator() in die Tonne kloppen willst.

    Bei mir hätte eine Matrix-Klasse selbst mit US-Tastaturlayout ausschliesslich den operator(). Es sieht zwar erstmal komisch aus, einem "Funktionsaufruf" etwas zuzuweisen - wenn man sich aber erstmal dran gewöhnt hat, ist die Schreibweise sehr angenehm. Besonders bei höherdimensionalen Arrays.

    Und falls du den operator[]-Proxy aufgibst - nicht frustriert sein! Du hast dabei immerhin was über Proxy-Klassen gelernt (sehr nützlich für fortgeschrittenes C++ und besonders, wenn man Bibliotheken schreibt) 😉



  • Ja, die eckigen Klammern mach ich immer altgr8 - altgr9 - altgr8 - altgr9 und dort dann wie Werte einfügen. Ist schon etwas umständlicher als shift8 - wert - komma - wert - shift9.
    Ich kann ja beides drin lassen, jetzt wo ich () auch etwas zuweisen kann.

    Ich überlege aber gerade, ob ich diese überhaupt groß benutzen werde. Mein Primärziel ist ja nur, zwei Matrizen zu multiplizieren. Das wird sich aber zeigen, wenn es um Spezialfälle wie eine Einheitsmatrix geht.

    Das Thema ist ja zum Glück noch nicht abgeschlossen 😉



  • @Finnegan sagte in Entwicklung einer einfachen Matrizenklasse:

    Ich will dich ja nicht in den Wahnsinn treiben, jetzt wo du dir mit deiner operator[]-Proxyklasse so viel Mühe gegeben hast.

    Apropos, das ging aber nur, weil mir hier so gut geholfen wurde.



  • @zeropage sagte in Entwicklung einer einfachen Matrizenklasse:

    Ja, die eckigen Klammern mach ich immer altgr8 - altgr9 - altgr8 - altgr9

    Tipp: die ansonsten nutzlose Feststelltaste (caps lock) kann man wunderbar zur Tastaturlayoutumstellung verwenden. ü und + sind [ und ], keine Shifts nötig mit US-Layout. Mit deutschem Tastaturlayout kann man nicht programmieren 🙂


Anmelden zum Antworten