Dipl.-Ing. Martin Weitzel, 64380 Roßdorf bei Darmstadt

TKI2019-09

Donnerstag "Denksport"-Aufgabe zu MultiThreading:

http://coliru.stacked-crooked.com/a/eb7ee896015649b6

Download-Link für Präsentations-Seiten (bitte Rechtsklick + Save-As)

  • von Montag
  • von Dienstag + Infografiken
    • RuntimeTypeIdentification (nur linke Seite
      Overhead rein virtueller Funktionen)
    • OpenClosedPrinciple
    • ExceptionBasics
    • SmartPointers
  • von Mittwoch
    • Vormittag + InfoGrafiken
      • STL-SequenceContainers
      • STL-AssociativeContainers
      • STL-ContainerIterators
      • STL-IteratorCategories
      • STL-IteratorUsage
    • Nachmittag (TBD)
  • von Donnerstag (TBD)

Dienstagmorgen (1. Aufgabe)

  • Fügen sie einige Tests für die Klassen `HourCounter` und `MinuteCounter` hinzu
  • Vereinigen Sie dann die beiden Klassen zu einer einzigen `ChainableCounter`
    • Halten sie die Obergrenze variabel (als Member-Daten-Element)
    • Hnweis: Sie benötigen dafür ein weiteres Konstruktor-Argumment
  • Bauen Sie aus dieser Klasse vier weitere "Uhren" testen Sie diese:
    • Sekunden (0..59) -> Minuten (0..59) -> Stunden (0..23)
    • Minuten -> Stunden -> Wochentage (letztere als Zahlen 0 .. 6)
    • Millisekunden -> Sekunden -> Minuten
  • Optional: Wie müssten Sie die `ChainableCounter`-Klasse anpassen, damit Sie auch die folgende Uhr (aus drei Objekten) aufbauen können.
    • Minuten (0..59) -> Stunden (1..12) -> AM/PM (als 0 oder 1)

Lösung (ohne AM/PM Clock): http://coliru.stacked-crooked.com/a/ba882c1815bcbc53

Dienstagmittag (2. Aufgabe)

  • Ersetzen Sie bisherige class `ChainableCounter` durch zwei Klassen, ähnlich denen die in der Schulungsunterlage als Beispiel verwendet wurden. (Sie müssen aber nicht exakt diese Klassen verwenden, sondern können diese inhaltlich so anpassen, wie es für den Zweck am besten ist.)
    • Eine Basisklasse soll in `count` das Zählen bis zu einer Grenze implementieren und dann wieder auf 0 zurückspringen.
    • Diese Basisklasse wird also EIN Konstruktor-Argument benötigen, welches die Grenze vorgibt, bis zu der gezählt wird.
    • Davon abgeleitet werden soll eine Klasse, welche per REFERENZ mit einer nächsten Zählstufe verbunden wird.
    • Diese Klasse muss in ihrem Konstruktor ZWEI Argumente bekommen (Grenze und nächste Stufe), davon das ERSTE an den Konstruktor der Basisklasse weiterreichen, das zweite zur Initialisierung der Referenz (für die nächste Zählstufe) verwenden.
    • Bauen und testen Sie damit ZUNÄCHST BITTE NUR einen zweistufigen Zähler (z.B. Minuten -> Stunden), da ein dreistufiger Zähler nur dann funktioniert, wenn Sie noch eine winzige Änderung vornehmen, die bisher noch nicht besprochen wurde.

Lösung: http://coliru.stacked-crooked.com/a/81b599f0f246a22f

Alternative Lösung: http://coliru.stacked-crooked.com/a/972ee9903c023da6
(mit "Non-Virtual-Interface = NVI-Idiom)

Mittwoch (1. Aufgabe)

  • Lokalisieren Sie im ersten Abschnitt (ehemals Header-File `NamedValue`) die Implementierungen vom Copy-Konstruktor und Move-Konstruktor.
    • Das Programm ist darauf vorbereitet dass durch Umschalten des `#if 0` auf `#if 1`) jede dieser Operationen einzeln erlaubt oder gesperrt werden kann.
  • Sperren Sie den Copy-Konstruktor (nur diesen), kompilieren Sie erneut und sehen Sie sich die Fehlermeldung an. Diese zeigen Ihnen, in welcher Operation der Copy-Konstruktor tatsächlich benötigt wird. (Das sollte hier nur an einer Stelle in `do_we_really_want_this` sein.)
  • Lassen Sie den Copy-Konstruktor gesperrt und kommentieren Sie die beanstandeten Zeilen aus, so dass Sie wieder fehlerfrei kompilieren können.
  • Sperren Sie anschließend auch den Move-Konstruktor, kompileren Sie erneut und sehen Sie sich die Fehlermeldungen an. Diese zeigen Ihnen, wo der Move-Konstruktor gebraucht wird. (Mehr als eine Stelle.)
  • Kommentieren Sie nun die Zeile 31 aus (die den Move-Konstruktor explizit verbietet) und kompilieren Sie erneut. Es gibt weiterhin Fehlermeldungen, aber die sehen nun etwas anders aus.
  • Aktivieren Sie nun wieder den Copy-Konstrultor (NICHT aber den Move-Konstrultor) indem Sie das entsprechende `#if 1` (das den Copy-Konstruktor derzeit explizit verbietet)  wieder auf `#if 0` setzen.
  • Es sollte nun alles wieder kompilierbar sein. Dies demonstriert, dass OHNE Move-Konstruktoren – an den Stellen an denen diese benötigt werden – der Compiler die (weniger effizienten) Copy-Konstruktoren verwenden würde.
    Dies dient AUCH der Rückwärts-Kompatibilität von C++11 zu C++98!
  • Ersetzen Sie die direkte Verwendung eines `const char *` zur Speicherung des Namens nun durch einen `std::unique_ptr<char[]>` und streichen Sie allen dadurch nicht mehr benötigten Code aus dem Programm
    • Streichen Sie AUCH die bedingt kompilierten Abschnitte, mit denen sich bislang zwischen gesperrten und explizit implementierten Copy- und Move-Konstruktoren wählen ließ.

LÖSUNG: http://coliru.stacked-crooked.com/a/ae75b096c770dea9

Mittwoch (2. Aufgabe): 

  • Ändern Sie den "Registrierungs-Container" `all` von einem klassichen Array (im C-Stil) um in ein `std::set<Resettable*>`.
    • Entfernen Sie die nicht mehr nötige Größenangabe aus der Template
      (= nur noch das Typ-Argument bleibt)
      Passen Sie sorgfältig alle zu ändernden Stellen an!
    • Trennen Sie die bisherige Funtion `reg` in zwei Funktionen auf:
      • `reg` zum "Anmelden" eines neuen Objekts
      • `unreg` zum Abmelden eines neuen Objekts.
    • Die beiden Funktionen werden dadurch viel sehr einfacher !!!
      • Es muss nur noch EIN Argument übergeben werden!
      • Es ist nur noch EIN (Member-) Funktionsaufruf notwendig:
        • `all.insert` zum eintragen
        • `all.erase` zum herauslöschen
          (Schlagen Sie die korrekte Nutzung dieser Funktionen ggf. im Internet cppreference.com nach)
  • OPTIONAL: Verwenden Sie nun statt des `std::set` nun einen `std::vector`
    • Strategie zum Anhängen: einfach all.push_back nehmen
    • Strategie zum Entfernen:
      • Eintrag suchen
      • Wenn NICHT letzter Eintrag dann
        • letzten Eintrag in nun freie Stelle kopieren
        • und anschließen letzten Eintrag entfernen

Lösung mit `std::set`: http://coliru.stacked-crooked.com/a/f5fed9d3dea03985

Lösung mit `std::vector`: http://coliru.stacked-crooked.com/a/e0c0592b2faeb455

Mittwoch (Live-Demo + Aufgabe + Lösung)

Links zu den kompilierbaren Beispielen dieser Schulung:

Arbeitsbereich:

Kleiner Programmrahmen zum zu Demo-Zwecken:

http://coliru.stacked-crooked.com/a/32431ca5ad205bab

Wann ist welcher Konstruktor erforderlich und wann wird er wirklich aufgerufen?