ABB2020-02
EC++FOR ABB Minden (zusätzliche Beispiele, Info-Graphiken und optionale Übungen)
Kapitel: =1= =2= =3= =4= =5= =6= =7= =8= =9= =10= =11= =12= =13= =14=
Zu Kapitel 1: Namespaces
- Beispiel zu Namespaces: https://godbolt.org/z/U8iqgQ
Zu Kapitel 2: Mehrfachvererbung
- InfoGraphic: DiamondShapedInhertance
- InfoGraphic: MultipleInheritance
- Beispiel zu override: https://godbolt.org/z/azmXJs
- Beispiel zu `using` innerhalb von Klassen: https://godbolt.org/z/w8TNn8
- Beispiel zu `sizeof` bei overlapping/disjoint: https://godbolt.org/z/dT8TCL
- Beispiel zu Konstruktoren bei overlapping/disjoint: https://godbolt.org/z/Lzqr3m
- Speicher-Mehrverbrauch durch virtuelle Basisklasse: https://godbolt.org/z/ongWJd
- Vergleich "disjoint" vs. "overlapping" im Beispiel `Counter`: https://godbolt.org/z/dyXVu und https://godbolt.org/z/M8zuJS
Zu Kapitel 3: Inheritance (incl. Multiple Inheritance / Interfaces)
- InfoGraphic: ClassToMemoryMapping
- InfoGraphic: RuntimeTypeIdentification
Zu Kapilel 4: Exceptions
- InfoGraphic: ExceptionBasics
- InfoGraphic: ExceptionDetails
- Startversion des `RingBuffer`:
https://godbolt.org/z/wuNoFa - Erste Ausbaustufe (`get`- / `put`-Fehleranzeige über Rückgabewert):
https://godbolt.org/z/RwBj-N - Zweite Ausbaustufe (`get`- / `put`-Fehleranzeige mit Exceptions):
https://godbolt.org/z/QNF7Fx - Dritte Ausbaustufe (Makro `T` zum Test der erwarteten Exception):
https://godbolt.org/z/qmcxbD
Optionale Übung:
Bauen Sie in dieses Beispiel zu Exceptions schrittweise folgende Änderungen ein:
- Vereinfachen Sie zunächst das Werfen von Exceptions indem Sie keine Klassen benutzen sondern
- z.B. den Typ `int` oder
- auch einen `enum`-Typ
- Entfernen Sie dabei zum Testen dieses Codes den Aufruf des `T`-Makros und verwenden Sie an dieser Stelle direkt die `try`-`catch`-Blöcke:
```
try {
b.put(1001); // <--- sixth `put` (that should cause an overflow)
std::cout << "how that? (expected that to throw)" << std::endl;
}
catch (int x) {
std::cout << "!!caught error #" << x << std::endl;
}
```
- Verwenden Sie anschließend wieder Klassen um Exceptions anzuzeigen (wie im Originalbeispiel):
- Leiten Sie `WillOverflow` und `WillUnderflow` nun aber von einer Klasse `RingBuffer_Exception` ab.
- Passen Sie den Test-Code an die erwarteten Exceptions an. (Bleiben Sie dabei, die erforderlich `try` - `catch`-Blöcke – so weit noch nötig – direkt in die Tests zu schreiben und nicht mittels des Makros `T` zu verwenden.)
LÖSUNG: https://godbolt.org/z/MSfzys
- OPTIONAL: Demonstrieren Sie, dass …
- … sich die tatsächlichen Exceptions nicht nur über ihre jeweilige Klasse abfangen lassen, sondern auch über die gemeinsame Basisklasse
- … bei mehreren `catch`-Blöcken der spezifischer (abgeleitete Klasse) vor dem allgemeineren (`RingBuffer_Exception`) erfolgen muss …
- … weil bei umgekehrter Reihenfolge der `catch`-Blöcke (Basisklasse zuerst) diese Vorrang hat.
- OPTIONAL: Die Basisklasse `RingBuffer_Exception` können Sie natürlich auch innerhalb der Klasse `RingBuffer_int_5` definieren, z.B. als Klasse `ExceptionBase` (was ihren Zweck als Basisklasse unterstreicht) …
```
class RingBuffer_int_5 {
…
public:
class ExceptionBase { /*no content so far*/ };
class WillOverflow : public ExceptionBase { /*nothing added*/ };
class WillUnderflow : public ExceptionBase { /*nothing added*/ };
…
};
```
oder innerhalb nur deklarieren und außerhalb definieren:
```
class RingBuffer_int_5 {
…
public:
class ExceptionBase;
class WillOverflow;
class WillUnderflow;
…
};
…
class RingBuffer_int_5::ExceptionBase { /*no content so far*/ };
class RingBuffer_int_5::WillOverflow
: public RingBuffer_int_5::ExceptionBase { /*nothing added*/ };
class RingBuffer_int_5::WillUnderflow
: public RingBuffer_int_5::ExceptionBase { /*nothing added*/ };
…
```- Passen Sie die Tests den neuen Erfordernissen an.
LÖSUNG: https://godbolt.org/z/Vyhc7A
und https://godbolt.org/z/XTvCdH
- Leiten Sie schließlich die Klasse `ExceptionBase` von einer Klasse aus der Standard-Exception-Hierarchie ab (Vorschlag: `std::runtime_error`) und geben Sie in deren Konstruktor einen Text an, welcher diese Klasse sinnvoll beschreibt:
- Hinweis: wenn dieser String in den beiden tatsächlich geworfenen Klassen unterschiedlich sein soll – was sinnvoll wäre(!) – muss der Konstruktor der Klasse `ExceptionBase` ein `const char*`-Argument haben, welches von in den Konstruktoren der endgültigen Klassen (`WillOverflow` und `WillUnderflow`) angegeben und von `ExceptionBase` an `std::runtime_error` "durchgereicht" wird.
- OPTIONAL: Verwenden Sie in den abgeleiteten Klassen statt eigener Konstruktoren die mit C++11 eingeführte "Konstruktor-Vererbung".
LÖSUNG: https://godbolt.org/z/Pc_ohy
Zu Kapitel 5: Dynamic Memory
- InfoGraphic: SmartPointers
- Einführung zu "copyable" vs. "move-only": https://godbolt.org/z/zujrO8
- OPTIONAL: C++98: `std::auto_ptr` (in C++11 "deprecated", in C++17 abgeschafft)
- OPTIONAL: Überladungs-Demo:
- Referenz vs. C++11-Rvalue-Referenz
- Klassische Referenz vs. `const`-Referenz
- Typischer Use-case: "copyable" vs. "move-only"
Optionale Übung:
Schauen Sie sich dieses Beispiel an und demonstrieren Sie (ggf. durch zusätzlich eingefügte Tests):
- `NamedValue_int` lässt sich verwenden wie ein `int`
(bedingt durch die automatische Typ-Umwandlung in eine `int`-Referenz):- Man kann damit "rechnen", neue Werte zuweisen usw. …
- … lediglich bei der Ausgabe, wird dem aktuellen Wert der Konstruktor angegebenen Bezeichner vorangestellt.
- `Named_value_int` Objekte sind nicht "copyable" aber "movable".
Bitte überlegen Sie zuerst selbst, wie einen Test auf "move only"-Typ aussehen könnte!
Wenn Sie keine eigene Idee haben, hier der Test-Code, den Sie verwenden könnten:
```
auto foo() {
return NamedValue_int{"returned-from-foo", -1};
}
void move_NamedValue_int_demo() {
NamedValue_int x{ "local in-main", 123 }; // OK
std::cout << x << std::endl;
// NamedValue_int y{ y }; // <---- ERROR (copying not supported!)
NamedValue_int y{ foo() }; // OK (move-operations defined)
std::cout << y << std::endl;
NamedValue_int z{ std::move(x) }; // <----- OK … BUT: from here on
std::cout << y << std::endl; // `x` is only destructable now
// std::cout << x << std::endl; // <--- and SHOULD NOT BE further used!
// x = z; // <-------------------- again: NO `copy` assignment ...
x = std::move(z); // but OK to do `move` assignment
std::cout << x << std::endl;
// std::cout << z << std::endl; // <--- should not be used any longer
}
```
(Und hier ist das entsprechend ergänzte Gesamtbeispiel.)
Optionale Übung zu `std::unique_ptr`
- Ersetzen Sie in der Klasse `NamedValue_int` …
- … das Member-Datenelement `const char* name;` durch
- den Smart-Pointer `std::unique_ptr<char[]> name;`
- Zeigen Sie, dass damit folgendes ersatzlos entfallen kann (oer sogar muss):
- der Destruktor, welcher `name` freigibt;
- der (implementierte) "move`-Cconstructor
- die (implementierte) "move"-Zuweisung
- nach wie vor ist die Klasse aber "movable"
- der (gesperrte) "copy`-Cconstructor
- die (gesperrte) "copy"-Zuweisung
- nach wie vor ist die Klasse aber nicht "copyable".
LÖSUNG: https://godbolt.org/z/XejqNB
- Fügen Sie für die nachfolgend genannten Operationen die passende `=delete`- oder `=default`-Definition ein:
- Konstruktor ohne Argumente (aka. Default Constructor);
- Kopier-Konstruktor
- Move-Construktor
- Kopier-Zuweisung
- Move-Zuweisung
- Destruktor
LÖSUNG: https://godbolt.org/z/ji6qwB
- OPTIONAL: Was ist noch hinzuzufügen, wenn Sie …
- … Kopieren (im Sinne eines "Klonens") unterstützden möchten?
- … ODER das Kopieren mit Namens-Anpassung?
– "abc" ===> "copy-of abc"
– "copy-of abc" ===> "copy-of copy-of abc"
LÖSUNG: https://godbolt.org/z/PXju7z
Zu Kapitel 6: State Machine
- Eweiterung der folgenden beiden Statemachines Decomment_FSM
- implementiert mit Kontrollstruktur (https://godbolt.org/z/yhFAmd)
- implementiert mit Tabelle (https://godbolt.org/z/tfxsM3)
- um die Zustände `ChrLiteral` und `QChrLiteral` (prinzpiell ganz ähnlich zu den bereits vorhandenen Zuständen `QStrLiteral` und `QStrLiteral`)
- `ChrLiteral` wird durch ein einfaches Anführungszeichen (') betreten und verlassen;
- `QChrLiteral` wird (von `ChrLiteral` aus) durch einen Gegenschrägstrich betreten und durch jedes beliebiges Folgezeichen wieder verlassen.
Zu Kapitel 9: Dynamischer Polymorphismus (RTTI)
- Info-Graphik: TypeBasedBranching
- Beispiele zu `const_cast`: https://godbolt.org/z/R9F6gh
Zu Kapitel 10: Templates
- Info-Graphik: TemplateBasics
- Info-Graphik: ParametrizingTypesAndSizes
- Info-Graphik: ReducingCodeBloat
- Systematischer Umbau des `NamedValue_int` Beispiels
in Template-Klasse (https://godbolt.org/z/ZpdyEQ)
Vergleich: Flexible Erweiterungspunkte mit virtuellen Funktionen oder Template:
- Info-Grafik: Example-OpenClosePrinciple
Optionale Übung:
Wandeln Sie in diesem Beispiel die Klasse `RingBuffer_int_5` schrittweise um in eine Template. (Nach jedem der Haupt-Schritte sollten Sie über eine kompilierbare Lösung verfügen!)
- Führen Sie für den generischen Typ das Symbol `T` und für die Größe das Symbol `N` ein, aber …
- … machen Sie die Klasse `RingBuffer_int_5` noch nicht zur Template!
- Definieren Sie die beiden Symbole `T` und `N` vielmehr über eine globale Konstante und eine globale Typdefinition:
```
// typedef int T; // <---- klassische C-Syntax ODER
using T = int; // <----- wahlweise neue C++11-Syntax
const std::size_t N = 6;
LÖSUNG: https://godbolt.org/z/n9VAYU
- ERST Wenn das Programm damit fehlerfrei kompiliert …
- gehen Sie mit "Suchen und Ersetzen" durch den Code und nehmen Sie folgende Umwandlungen vor
RingBuffer_int_5 ===> RingBuffer<T, N>
- gehen Sie mit "Suchen und Ersetzen" durch den Code und nehmen Sie folgende Umwandlungen vor
- ausgenommen
- im Namen der Klasse selbst (also direkt dort wo sie definiert wird);
- wenn der Klassenname als Konstruktor oder Destruktor auftrifft;
- nirgendwo in der Funktion `basic_RungBuffer_demo` (dazu später).
- In dieser Form wird der Code (noch) nicht kompilieren!
- Fügen Sie nun vor der Definition der Klasse selbst
und vor der Definition jeder Member-Funktion außerhalb der Klasse folgende Zeile ein
- Fügen Sie nun vor der Definition der Klasse selbst
```
template <typename T, std::size_t N>
```
(Damit erklären Sie dem Compiler, dass im direct nachfolgenden Code { … }-Block `T` für einen beliebigen Typnamen und `N` für einen für eine beliebige konstante vom Typ `std::size_t` steht. Die beiden Zeilen, mit der Sie die Bezeichner `T` und `N` dem Compiler vorher bekannt gemacht hatten, können Sie nun löschen.)
- In dieser Form wird wird der Code (noch) nicht kompilieren!
- Fügen Sie schließlich in die Funktion `basic_RingBuffer_demo` folgendes ein:
```
// typedef RingBuffer<int, 5> RingBuffer_int_5; // <---- klassische C-Syntax ODER
using RingBuffer_int_5 = RingBuffer<int, 5>; // <--- alternativ neue C++11-Syntax
```
(Alternativ können Sie auch jedes Vorkommen des Names `RingBuffer_int_5` in der Funktion `basic_RingBuffer_demo` ersetzen durdh `RingBuffer<int, 5>`, also "fast" wir zuvor geschehen aber dieses Mal mit konkreten Typen, nicht allgemeinen Symbolen. Die Verwendung einer Typdefinition wie oben vorgeschlagen ist jedoch einfacher und sicherer: sie ist schneller erledigt und man kann nichts übersehen.)
- Jetzt sollte der Code wieder kompilieren!
- Testen Sie nun noch mehrere Varianten der `RingBuffer`-Template.
- Sie können dazu einfach den Code von `basic_RingBuffer_demo` kopieren und die notwendigen Änderungen vornehmen (wobei Sie daran denken sollten, dass dies weitere Anpassungen nach sich zieht).
LÖSUNG: https://godbolt.org/z/WrrkiE
- Wenn Sie den "payload"-Datentyp ändern, müssen Sie auch die mit `put` abgespeicherten und mit `get` ausgelesenen Werte-Typen entsprechend anpassen.
- Wenn Sie die Maximalzahl der Elemente im Puffer ändern, müssen Sie auch die Anzahl der `put` und/oder `get`-Aufrufe (bis der Puffer voll oder leer ist) entsprechend anpassen.
LÖSUNG: https://godbolt.org/z/hfNZrv
Zu Kapitel 11: STL
- Container
- Info-Graphik: STL-SequenceContainers
- Info-Graphik: STL-AssociativeContainers
- Iterators
- Info-Graphik: STL-IteratorUsages
- Info-Graphik: STL-ContainerIterators
- Info-Graphic: STL-IteratorDetails
- Info-Graphik: STL-IteratorCategories
- Algorithms
- Info-Graphik: AlgorithmicComplexity
- Some Live-Examples: ??????
Zu Kapitel 12: Programmierung von Parallelen Systemen
Zu Kapitel 13: Hardware-Abstraktion und Treiber mit C++
Zu Kapitel 14: Übungen