DIGITAL
Dieses Aufgabenblatt soll eine Einführung in die Objektorientierte Programmierung darstellen.
#include
d man also Code, sofern sich dieser in einer anderen Datei befindet. In dieser Aufgabe schauen wir uns an, welche Probleme dabei entstehen können und wie man diese Lösen kann.
main.cpp
abzuändern, sodass das Problem nicht mehr auftauchen würde, nämlich indem man die Zeile #2, #include "B.h"
aus der main.cpp
entfernt. Verifizieren Sie zuerst, dass diese Änderung eine Kompillierung des ursprünglichen Codes ermöglicht. A.h
#include "<iostream>"
#include "B.h"
struct A {
B b;
A(B _b) : b(_b) {}
void print() {
std::cout << "B=";
b.print();
}
};
B.h
#include <iostream>
struct B {
int val;
B(int _val) : val(_val) {}
void print() {
std::cout << "{val = " << val << "}";
}
};
main.cpp
#include "A.h"
#include "B.h"
#include <iostream>
int main() {
B b(42);
A a(b);
b.print();
std::cout << "\n";
a.print();
return 0;
}
left.h
#include "right.h"
struct Left {
Right right;
Left(Right _right) : right(_right) {}
};
right.h
#include "left.h"
struct Right {
Left left;
Right(Left _left) : left(_left) {}
};
main.cpp
#include "left.h"
#include "right.h"
#include <iostream>
int main() {
}
Car
) später definiert wird und stellen nur sicher, dass der Compiler weiß, dass dieses Objekt existiert (wir müssen nur sicherstellen, dass die Klasse später genauer definiert wird). Der Nachteil hierbei ist, dass wir bis die Klasse genauer definiert wird, auf keine Details (Member, Speichergröße) zurückgreifen dürfen. Reine Pointer sind jedoch vollkommen okay, da der Speicher dafür stets durch size_t
gegeben ist. Die Syntax dafür sieht folgendermaßen aus: class Car; // forward declaration
class Wheel {
Car* car;
};
left.h
#include "right.h"
struct Left {
Right *right;
Left(Right *_right) : right(_right) {}
};
right.h
#include "left.h"
struct Right {
Left *left;
Right(Left *_left) : left(_left) {}
};
main.cpp
#include "left.h"
#include <iostream>
int main() {
}
Nun zur Aufgabe:
Für ein besonderes Bauteil werden mehrere Holzprofile zu einem Verbundquerschnitt verleimt (insbesondere überlappen sich die Querschnitte nicht). Folgende drei Querschnitte sollen verwendet werden:
Die Aufgabe ist, eine objektorientierte Klassehierarchie zu definieren, die das obige Setting representieren würde:
flaeche
zur Ermittlung der Querschnittsfläche enthält.std::vector<Querschnitt*>
speichern mit Pointern auf die individuellen Teilquerschnitte. Überlegen Sie sich, wie man hier flaeche
sinnvoll implementieren kann.main()
-Methode all Ihre Klassen erzeugt und deren Methode flaeche
testet.Auf den vergangenen Blättern haben wir den []
-Operator zur Indizierung von sowohl C-Arrays als auch selbst definierten Datenstrukturen benutzt. Da C-Arrays aber nichts anderes als zusammenhängende Speicherblöcke sind, kann man auch direkt mithilfe von Pointerarithmetik über C-Arrays iterieren. In diesem Fall sieht man oft die Konvention, dass das Array durch einen Pointer auf das erste Element und einen Pointer hinter das letzte Element begrenzt wird. Die Länge ist dadurch implizit durch die Differenz der beiden Pointer gegeben. Diese Darstellung bietet sich besonders auch dann an, wenn man nur Teile des Arrays betrachten will.
uint32_t buffer[10] {1, 3, 3, 7, 4, 2, 3, 6, 0, 0};
// points to first element
uint32_t *begin = buffer + 2;
// points to the next position after the last element
uint32_t *end = buffer + 5;
for (uint32_t *p = begin; p != end; ++p) {
std::cout << *p << "\n";
}
LinkedLists bieten a priori keine Möglichkeit durch einfache Pointerarithmetik über Elemente zu iterieren; Dies liegt daran, dass die Elemente einer DoublyLinkedList sich an beliebigen (nicht aufeinanderfolgenden) Positionen im Speicher befinden können. (Jeder Knoten wird separat durch new
Instanziiert — bei einem Array wird etwa per new T[10]
direkt ein ganzer Speicherblock reserviert).
In dieser Aufgabe implementieren Sie eine Klasse, die sich nach außen hin wie ein Pointer verhält, aber intern stattdessen über die im Speicher verteilten Elemente (hier einer DoublyLinkedList) iteriert. Ein solches Konstrukt wird im Allgemeinen Iterator genannt.
Im Repository haben wir für Sie bereits eine Implementierung für eine DoublyLinkedList hinterlegt. Die Deklaration des Iterations ist für diese Aufgabe bereits vorgegeben, sie gehört zu den existierenden Deklarationen in den DoublyLinkedList-Header:
class LinkedListIterator {
// all the variables you need go here
public:
LinkedListIterator(/* ... arguments ... */);
int& operator*();
int* operator->();
// pre-increment
LinkedListIterator& operator++();
// post-increment
LinkedListIterator operator++(int);
// non-standard für LinkedLists
LinkedListIterator& operator+=(size_t amount);
// non-standard, very useful for LinkedLists
void insert_after(int element);
};
bool operator ==(const LinkedListIterator &lhs, const LinkedListIterator &rhs);
bool operator !=(const LinkedListIterator &lhs, const LinkedListIterator &rhs);
LinkedListIterator begin(DoublyLinkedList &l);
LinkedListIterator end(DoublyLinkedList &l);
LinkedListIterator
s. begin
gibt hierbei einen Iterator zurück, der auf das erste Element der DoublyLinkedList zeigt.end
gibt einen Iterator zurück, der "hinter" das letzte Element der DoublyLinkedList zeigt - ein solches Element existiert natürlich nicht, überlegen Sie sich daher, wie ein solcher Iterator aussehen könnte.l
leer, so muss notwendigerweise begin(l) == end(l)
wahr sein.DoublyLinkedList l;
l.append(1);
l.append(2);
l.append(3);
l.append(4);
l.append(5);
l.append(6);
LinkedListIterator b = begin(l);
++b;
++b;
b.insert_after(7);
LinkedListIterator e = end(l);
for (LinkedListIterator p = b; p != e; ++p) {
std::cout << *p << "\n";
}
void insert_ones_after_zeros(LinkedListIterator first, LinkedListIterator last)
. Die beiden Iteratoren first
und last
begrenzen wie oben erklärt einen Teil einer DoublyLinkedList. Nutzen Sie die Iteratoren, um zwischen first
und last
eine 1 hinter jede 0 in der DoublyLinkedList einzufügen. DoublyLinkedList l;
l.append(0);
l.append(4);
l.append(0);
l.append(2);
l.append(0);
l.append(3);
LinkedListIterator b = begin(l);
++b;
++b;
LinkedListIterator e = end(l);
insert_ones_after_zeros(b, e);
// l sollte jetzt die Elemente 0, 4, 0, 1, 2, 0, 1, 3 enthalten.
// Hinter die erste 0 wird keine 1 eingefügt, weil b auf das dritte Element zeigt und dadurch die erste 0 nicht zwischen b und e liegt.