PDF-Datei

Transcription

PDF-Datei
FH Wiesbaden
FB DCSM
Aufgaben zum Praktikum
zur Vorlesung Programmieren in C++, WS 2007/2008
Prof. Dr. Dreher
4. AUFGABENBLOCK
Aufgabe 5.
Eine Template-Klasse für dynamische n x m Matrizen mit überladenen Operatoren
In der Vorlesung wurde die Definition einer Vektor-Klasse Vector für ints besprochen. Orientieren Sie sich daran (und am weiteren Verlauf der Vorlesung), um eine entsprechende
Template-Klasse für einen Vektor aus beliebigen Datentypen T zu implementieren. Hier
(nur) die (vorläufige) Deklaration der Klasse:
template <class T>
class Vektor {
public:
explicit Vektor(int n);
~Vektor();
T& element(int i);
int num_elem() const;
private:
T* p;
int
size;
};
//
//
//
//
Konstruktor für n Elemente
Destruktor
Zugriff auf i-tes Element
Anzahl Elemente
Verwenden Sie diese (später entsprechend zu modifizierende) Klasse, um eine TemplateKlasse Matrix für dynamische n x m Matrizen zu erstellen, die Elemente vom Typ T enthalten, wobei diese so implementiert ist, daß eine n x m Matrix aus n Vektoren zu je m Elementen besteht, d.h. jede der n Zeilen wird durch einen Vektor aus m Elementen dargestellt.
Dies kann so implementiert werden, daß Matrix als dynamische Datenstruktur ein Array aus
Zeigern auf (ebenfalls dynamisch erzeugte) Vektoren enthält.
Die vorläufige(!) Deklaration von Matrix hat die folgende Form:
template <class T>
class Matrix {
public:
Matrix(int n, int m);
~Matrix();
T& element(int i, int j);
int num_rows() const;
int num_cols() const;
private:
Vektor<T>** p;
int
rows;
int
columns;
};
//
//
//
//
//
Konstruktor für n x m Elemente
Destruktor
Zugriff auf i-tes Element
Anzahl Zeilen
Anzahl Spalten
// Array von Zeigern auf Objekte Vektor
Implementieren Sie die Klasse Matrix unter Beachtung der folgenden zusätzlichen Punkte:
•
Implementieren Sie den Zugriff auf die Matrixelemente nicht über die oben angegebene
Methode element(int, int), sondern duch Überladen des Index-Operators, so dass auf die
Matrixelemente in der in C++ üblichen Indexschreibweise, z.B. m[5][7], zugegriffen werden kann (bzw. (*m)[5][7], wenn m ein Zeiger auf eine Matrix ist).
- 12 -
19.11.2007
FH Wiesbaden
FB DCSM
Aufgaben zum Praktikum
zur Vorlesung Programmieren in C++, WS 2007/2008
Prof. Dr. Dreher
–
Um dies realiseren zu können, nutzen Sie aus, daß eine Matrix aus Vektoren besteht.
Sie enthält so viele Vektoren, wie sie Zeilen hat, jeder Vektor hat soviele Elemente
wie die Matrix Spalten besitzt.
–
Der Indexoperator der Matrix (erste Indexposition) liefert eine Referenz auf den entsprechenden Vektor zurück, der zweite Indexoperator hat als Operanden diese Vektorreferenz und liefert selbst eine Referenz auf das entsprechende Element des Vektors zurück (vom Typ T).
–
Aus diesem Grund müssen Sie auch für die Klasse Vektor den Indexoperator überladen!
•
Der Konstruktor für Matrix hat zwei Parameter, wobei der erste die Anzahl der Zeilen, der
zweite die Anzahl der Spalten der Matrix angibt.
•
Implementieren Sie zusätzlich eine Methode init(), die die Matrix mit irgendwelchen
Werten initialisiert, z.B. mit Zeilen- und Spaltenindex als zusammengesetzte Zahl interpretiert.
•
Überladen Sie den Ausgabeoperator, so dass eine Matrix wie ein Standarddatentyp in geeigneter Weise ausgegeben werden kann:
cout << *m; // m ist Zeiger auf eine Matrix
–
Um dies zu realisieren überladen sie zunächst den << Operator der Klasse Vektor.
Bei der Implementierung des Überladens desselben Operators für Matrix können Sie
dann bereits von dem überladenen Operator der Klasse Vektor Gebrauch machen, indem sie für jede Zeile der Matrix einen Vektor ausgeben.
•
Überladen Sie den Multiplikationsoperator, so dass zwei Matrizen a und b ebenfalls in der
üblichen Schreibweise miteinander multipliziert werden können und das Ergebnis einer
(existierenden und geeignet dimensionierten) Matrix c zugewiesen werden kann:
c = a * b;
bzw.
*c = *a * *b; // a, b und c Zeiger auf Matrizen
–
Um dies zu realisieren, müssen Sie neben dem Überladen des Operators * für ein
Produkt aus zwei Matrizen auch für beide Klasse Matrix und Vektor geeignete Kopierkonstruktoren definieren und ihre Zuweisungsoperatoren überladen. Nur dadurch
können Sie sicherstellen, daß nicht nur die Attributwerte der Klassen sondern auch
die mit den Klassen verbundene dynamische Datenstrukturen kopiert und zugewiesen werden.
–
Stellen Sie auch hier durch Zusicherungen sicher, daß die Zeilen- und Spaltenzahlen
der beteiligten Matrizen so geschaffen sind, daß die gewünschte Operation durchgeführt werden kann.
–
Auch hier müssen alle Matrix-Objekte vor Aufruf in der korrekte Größe existieren.
–
Die Funktion wird sinnvollerweise in der Header-Datei von Matrix deklariert.
Prüfen Sie alle möglichen Fehlerzustände durch Zusicherungen (Makro assert(); deklariert in
der Include-Datei <cassert>) und fangen Sie mögliche Exceptions.
Schreiben Sie ein Hauptprogramm mit den folgenden Funktionsschritten:
•
Lesen sie die vom Benutzer gewünschte Zeilen- und Spaltenzahl ein.
•
Erzeugen sie dynamisch ein entsprechendes Matrix-Objekt m und initialisieren es mit der
Methode init() und geben es mit print() aus.
- 13 -
19.11.2007
FH Wiesbaden
FB DCSM
Aufgaben zum Praktikum
zur Vorlesung Programmieren in C++, WS 2007/2008
Prof. Dr. Dreher
•
Erzeugen Sie ein zweites Matrix-Objekt n von geeigneter Größe, so daß das Produkt m *
n gebildet werden kann.
•
Erzeugen Sie ein drittes Matrix-Objekt mn, das das Produkt aus m mal n aufnehmen kann.
•
Berechnen Sie das Produkt mn als m mal n mittels mmpy() und geben es aus.
•
Führen Sie die Ausgabe sowohl über den überschriebenen Ausgabeoperator als auch
durch Ausgabe jedes Elementes der Matrix einzeln (über Index-Zugriffe) durch.
•
Vernichten Sie alle dynamisch allokierten Instanzen.
Beobachten Sie mit dem Debugger, daß beim Vernichten der Objekte auch die internen Strukturen mit Hilfe des Destruktors freigegeben werden.
Teilen Sie die einzelnen Teile des Programms in der folgenden Form projektmäßig auf.
Von der C++ Sprachdefinition sollte das möglich sein:
•
MatMain.cpp
Hauptprogramm
•
Vektor.h
Deklaration der Klasse Vektor
•
Vektor.cpp
Implementierung der Klasse Vektor
•
Matrix.h
Deklaration der Klasse Matrix
• Matrix.cpp
Implementierung der Klasse Matrix
Leider erlauben die meisten existierenden Compiler dies nicht, wenn in den cpp-Dateien
Template-Klassen implementiert sind. Die entsprechenden Instanzen der Methoden, die ja nur
im Hauptprogramm angefordert werden, werden dann nicht gefunden.
Nicht sehr eleganter Ausweg ist, alle Methoden in den Header-Dateien zu implementieren.
Auszug aus http://www.comeaucomputing.com/techtalk/templates/#whylinkerror (xyz ist die
Template-Klasse):
One way around this is to explicitly request the instantiation of xyz, in this example of
xyz<int>. In a brute force effort, this could be added to xyz.cpp by adding this line at
the end of it:
template xyz<int>;
which requests that (all of) xyz<int> be instantiated. That's kind of in the wrong place
though, since it means that everytime a new xyz type is brought about that the implementation file xyz.cpp must be modified.
A less intrusive way to avoid that file is to create another:
// xyztir.cpp
#include "xyz.cpp" // .cpp file!!!, not .h file!!
template xyz<int>;
This is still somewhat painful because it still requires a manual intervention everytime a new
xyz is brought forth. In a non-trivial program this could be an unreasonable maintenance
demand.
- 14 -
19.11.2007
FH Wiesbaden
FB DCSM
Aufgaben zum Praktikum
zur Vorlesung Programmieren in C++, WS 2007/2008
Prof. Dr. Dreher
So instead, another way to approach this is to #include "xyz.cpp" into the end of
xyz.h:
// xyz.h
// ... previous content of xyz.h ...
#include "xyz.cpp"
You could of course literally bring (cut and paste it) the contents of xyz.cpp to the end of
xyz.h, hence getting rid of xyz.cpp; it's a question of file organization and in the end the
results of preprocessing will be the same, in that the method bodies will be in the header, and
hence brought into any compilation request, since that would be using the respective header.
Either way, this has the side-effect that now every template is in your header file. It could
slow compilation, and it could result in code bloat. One way to approach the latter is to declare the functions in question as inline, so this would require you to modify xyz.cpp in the
running example.
- 15 -
19.11.2007

Documents pareils