Content

Repräsentierung einer Matrix mit einer struct

In den bisherigen Funktionen, die mit Matrizen operierten, wurden diese durch eine Reihe von Parametern charakterisiert:

void
initMatrix(long m, long n, double *A, long incRowA, long incColA) {
   /* ... */
}

Diese lassen sich sowohl in C als auch in C++ zu einer sogenannten struct zusammenfassen:

struct Matrix {
   long m;
   long n;
   double* A;
   long incRowA;
   long incColA;
};

Da es nicht sinnvoll erscheint, den Namen A generell mit dem einer Matrix zu verbinden, wird dieser gerne weggelassen. Der Zeiger auf die Daten wird dann einfach data genannt:

struct Matrix {
   long m;
   long n;
   double* data;
   long incRow;
   long incCol;
};

Wenn hier der erste Schritt zu C++ genommen wird, ist zu beachten, dass bei einigen Implementierungen long kleiner sein kann als die Größe einer Matrix. Sowohl in C als auch C++ lässt sich dafür size_t verwenden (ohne Vorzeichen) oder auch alternativ ptrdiff_t (mit Vorzeichen). In C++ gibt im Gegensatz zu C Namensräume und alle Namen aus der Standardbibliothek sind im Namensraum std. Entsprechend verwenden wir hier std::size_t anstelle von size_t:

#include <cstddef>

struct Matrix {
   std::size_t m;
   std::size_t n;
   std::size_t incRow;
   std::size_t incCol;
   double* data;
};

C++ baut auf C auf und erlaubt auch die Nutzung von Typen und Funktionen aus der Standard-Bibliothek für C. Es werden aber jeweils andere Header-Namen verwendet. Aus #include <stddef.h> in C wird in C++ hier #include <cstddef>. Analog kann auch std::printf verwendet werden nach #include <cstdio>.

C++ ist eine sogenannte objekt-orientierte Programmiersprache, die es erlaubt, Funktionen mit einer einer struct (oder class) zu verknüpfen. Diese Funktionen werden Methoden genannt. Eine Methode init analog zum zuvor vorgestellten initMatrix könnte dann so aussehen:

struct Matrix {
   std::size_t m;
   std::size_t n;
   std::size_t incRow;
   std::size_t incCol;
   double* data;

   void init() {
      for (std::size_t i = 0; i < m; ++i) {
         for (std::size_t j = 0; j < n; ++j) {
            data[i*incRow + j*incCol] = j * n + i + 1;
         }
      }
   }
};

So könnte dazu passend eine Initialisierung und ein Aufruf der Methode aussehen:

Matrix A;
A.m = 7; A.n = 8;
A.data = new double[A.m * A.n];
A.incRow = 1; A.incCol = 7;
A.init();

Ein Methodenaufruf besteht immer aus einem Ausdruck, der ein Objekt liefert (im Beispiel A), dem Namen der Methode (hier init) und der Parameterliste (hier leer). Das referenzierte Objekt wird dabei zu einem impliziten Parameter. Wenn innerhalb der Methode init auf eine Komponente der struct zugegriffen wird wie etwa auf data, dann bezieht sich das immer auf das beim Aufruf der Methode angegebene referenzierte Objekt.

Aufgabe

Passen Sie das folgende Beispiel so an, dass aus der Funktion print_matrix eine Methode print wird.

Vorlage

#include <cstddef> /* needed for std::size_t */
#include <cstdio> /* needed for printf */

struct Matrix {
   std::size_t m; /* number of rows */
   std::size_t n; /* number of columns */
   std::size_t incRow;
   std::size_t incCol;
   double* data;

   void init() {
      for (std::size_t i = 0; i < m; ++i) {
         for (std::size_t j = 0; j < n; ++j) {
            data[i*incRow + j*incCol] = j * n + i + 1;
         }
      }
   }
};

void print_matrix(std::size_t m, std::size_t n,
      const double* A, std::size_t incRowA, std::size_t incColA) {
   for (std::size_t i = 0; i < m; ++i) {
      std::printf("  ");
      for (std::size_t j = 0; j < n; ++j) {
         std::printf(" %4.1lf", A[i*incRowA + j*incColA]);
      }
      std::printf("\n");
   }
}

int main() {
   Matrix A;
   A.m = 7; A.n = 8;
   A.data = new double[A.m * A.n];
   A.incRow = 1; A.incCol = 7;
   A.init();
   std::printf("A =\n");
   print_matrix(A.m, A.n, A.data, A.incRow, A.incCol);
}

Übersetzen und Ausführen

$shell> g++ -std=gnu++11 -o matrix_class1 matrix_class1.cpp
$shell> matrix_class1
A =
    1.0  9.0 17.0 25.0 33.0 41.0 49.0 57.0
    2.0 10.0 18.0 26.0 34.0 42.0 50.0 58.0
    3.0 11.0 19.0 27.0 35.0 43.0 51.0 59.0
    4.0 12.0 20.0 28.0 36.0 44.0 52.0 60.0
    5.0 13.0 21.0 29.0 37.0 45.0 53.0 61.0
    6.0 14.0 22.0 30.0 38.0 46.0 54.0 62.0
    7.0 15.0 23.0 31.0 39.0 47.0 55.0 63.0