========================================= Zugriffmethoden auf ein einzelnes Element ========================================= Es erscheint sinnvoll, einfache Zugriffsmethoden anzubieten, um auf ein einzelnes Element einer Matrix lesenderweise oder schreibenderweise zuzugreifen. Entsprechende Methoden könnten wie folgt aussehen: ---- CODE (type=cpp) ---------------------------------------------------------- struct Matrix { /* ... */ double get(std::size_t i, std::size_t j) const { return data[i*incRow + j*incCol]; } void set(std::size_t i, std::size_t j, double value) { data[i*incRow + j*incCol] = value; } /* ... */ }; ------------------------------------------------------------------------------- Das `const` hinter der Parameterliste bei der `get`-Methode bedeutet, dass die entsprechende Methode den Zustand der Matrix nicht verändert. Sie kann auch dann aufgerufen werden, wenn nur eine Referenz auf eine Matrix mit Leserechten vorliegt, also beispielsweise `const Matrix&`. Die Nutzung würde dann beispielsweise so aussehen: ---- CODE (type=cpp) ---------------------------------------------------------- if (A.get(2, 3) > 0) { A.set(2, 3, -1); } ------------------------------------------------------------------------------- In C++ geht dies jedoch auch sehr viel eleganter unter Verwendung von Referenzen. Nicht nur Parameter können einen Referenzparameter haben, auch für den Rückgabewert ist dies zulässig. Wenn eine Referenz zurückgegeben wird, kann der Methodenaufruf sowohl auf der linken als auch auf der rechten Seite einer Zuweisung stehen: ---- CODE (type=cpp) ---------------------------------------------------------- struct Matrix { /* ... */ double& access(std::size_t i, std::size_t j) { return data[i*incRow + j*incCol]; } /* ... */ }; ------------------------------------------------------------------------------- So könnte dann die Nutzung aussehen: ---- CODE (type=cpp) ---------------------------------------------------------- if (A.access(2, 3) > 0) { A.access(2, 3) = -1; } ------------------------------------------------------------------------------- Somit kann `A.access(i, j)` wie jede andere Variable auch verwendet werden. In C++ wird die Schreibweise typischerweise mit Hilfe des Überladens des Funktionsoperators noch weiter vereinfacht. Vom Überladen wird gesprochen, wenn der gleiche Operator für verschiedene Datentypen verwendet werden kann. So ist der Operator `+` sowohl für `int` als auch `double` zulässig und funktioniert in jeweils unterschiedlich. In C++ ist es möglich, diese Überladungen auch auf eigene Datentypen zu erweitern. Dies geht für sämtliche Operatoren von C++. Auch der Funktionaufruf, d.h. der Operator `()` lässt sich überladen. Überladene Operatoren können wie normale Funktionen oder Methoden definiert werden, nur wird der Name dann durch `operator` und dem jeweiligen Operatorsymbol ersetzt. So lässt sich die `access`-Methode durch einen Operator ersetzen: ---- CODE (type=cpp) ---------------------------------------------------------- struct Matrix { /* ... */ double& operator()(std::size_t i, std::size_t j) { return data[i*incRow + j*incCol]; } /* ... */ }; ------------------------------------------------------------------------------- Die Nutzung wäre dann so möglich: ---- CODE (type=cpp) ---------------------------------------------------------- if (A(2, 3) > 0) { A(2, 3) = -1; } ------------------------------------------------------------------------------- Wenn diese Zugriffsmethode mit Referenz sowohl bei schreibbaren als auch nur zum Lesen zur Verfügung stehenden Matrizen benutzbar sein soll, benötigen wir beide Varianten: ---- CODE (type=cpp) ---------------------------------------------------------- struct Matrix { /* ... */ const double& operator()(std::size_t i, std::size_t j) const { return data[i*incRow + j*incCol]; } double& operator()(std::size_t i, std::size_t j) { return data[i*incRow + j*incCol]; } /* ... */ }; ------------------------------------------------------------------------------- Der Übersetzer wählt dann vollautomatisch das aus, was jeweils benötigt wird. Wenn Sie möchten, können Sie die Zugriffsmethoden auch mit einer Überprüfung der Indizes ausstatten. Dies geht mit Hilfe der Funktion `assert` aus `#include `: ---- CODE (type=cpp) ---------------------------------------------------------- #include /* ... */ struct Matrix { /* ... */ const double& operator()(std::size_t i, std::size_t j) const { assert(i < m && j < n); return data[i*incRow + j*incCol]; } double& operator()(std::size_t i, std::size_t j) { assert(i < m && j < n); return data[i*incRow + j*incCol]; } /* ... */ }; ------------------------------------------------------------------------------- Aufgabe ======= Ergänzen Sie die Vorlage durch eine Kopierfunktion mit folgender Signatur und verwenden Sie dazu die oben definierten Zugriffsmethoden: ---- CODE (type=cpp) ---------------------------------------------------------- void copy_matrix(const Matrix& A, Matrix& B) { /* copy A to B */ } ------------------------------------------------------------------------------- Überlegen Sie sich oder ggf. testen Sie, welche der Zugriffsmethoden bei welcher der Matrizen hierfür aufgerufen werden. Vorlage ======= :import: session7/matrix_class5.cpp :navigate: up -> doc:index back -> doc:session7/page06 next -> doc:session7/page08