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

bosch2019-05

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

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

Sortierung = aktuelles Kapitel jeweils am Anfang / innerhalb Kapitels von oben nach unten


Kapitel 08 - Spezielle Member-Funktionen (Nachtrag Move-Semantik)

...


Kapitel 13 - Die Standard Template Library (STL)

Demo dynamic_cast: http://coliru.stacked-crooked.com/a/28fee908dac93139

und alternatives Design mit zusätzlichen viruellen Member-Funktionen in der Top-Level Klasse: TBD

Demo sequentielle Container: http://coliru.stacked-crooked.com/a/2d04c40df5421bcc.

Demo zu sortierender Container: http://coliru.stacked-crooked.com/a/f76202d197b7ee75


Kapitel 12 - Streams

...


Kapitel 11 - Das Schlüsselwort friend


Kapitel 10 - Virtuelle Methoden


Donnerstagmorgen - Wiederholung zu Mittwoch (inkl. kleinere Ergänzungen)

Wiederholung zur Vererbung:

Eine wichtige Rolle in der Objektorientieten Programmierung (OOP) spiet das "Liskovsche Ersetzungsprinzip" oder kurz LSP (= Liskov Substitution Principle)

In der umgansprachlichen Formulierung besagt es, dass

  • ein Objekt einer abgeleiteten Klasse an die Stelle seiner Basisklasse treten kann, ohne dass es bei der Arbeit mit diesem Objekt zu berücksichtigt werden muss.
  • In C++ wid das LSP insofern direkt unterstützt, als mit zwei Klassen
    class Base { ... };
    class Derived : public Base { ... };
  • und entsprechenden Objekten die folgenden Anweisungen direkt (ohne Cast-Operation) akzeptiert werden:
    Derived d;
    Base b = d;  // slicing: nur der Derived-Anteil von `d` wird in `b` kopiert
    Base *bp = &d;  // Compilezeit-Typ: `Base`-Zeiger aber Laufzeittyp `Derived`-Zeiger 
    Base &br = d;  // Compillezeit-Typ: `Base`-Referenz aber Lauffzeit-Typ `Derived&`

Sehen Sie sich das folgende (Coliru-) Code Beispiel an

http://coliru.stacked-crooked.com/a/640b0fc51acdaf68

und ermitteln Sie in den Funktionen in Bezug auf die Klassen `Base` und `Derifed jeweils eine Siuation besteht, in der sich Compilezeit-Typ und Laufzeit-Typ unterscheiden, und zwar

  • in `demo1` bezüglich einer lokalen Variablen (ganz offenichtlich)
  • in `demo2` bezüglich eines Fuktionsarguments (auch noch sehr offensichltlich)
  • in `demo3` bezüglich des`this`-Zeigers (nicht ganz so offensichtlich)
  • `demo4` und `demo5` bitte noch außer Acht lassen (wird erst später benötigt!)

Entfernen Sie nun nacheinander in `main` von jeweils einer dieser drei Funktionen den Kommentar (so dass diese Funktion aufgerufen wird) und bestimmen Sie in bezug auf die Member-Funktion `m` ob diese

  • gemäß dem Laufzeit-Typ oder
  • gemäß dem Compilezeit-Typ

ausgeführt wird.

Motivation `virtual` für Member-Funktionen:

Wiederholen Sie nun das Obige

  • nachdem Sie vor der Definition der Member-Funktion `m` in der Klasse `Base` das Schlüsselwort virtual ergänzt haben. (nur für `demo1` bis `demo3` - `demo4` und `demo5` kommt erst später dran.)
  • nachdem Sie den Übergabe-Parameter für `f` von einem Referenz-Argument in ein Wert-Argument geändert haben (Hat eine Auswirkung nur für `demo2`.)

Analysieren Sie nun die Verkettung von `Base` und `Chainable` für demo5`.

  • Bevor Sie das Program mit atviertem `demo5` erneut kompileren und ausführen, versuchen Sie eine Vorhersage wie die Ausgabe aussehen wird mit und ohne virtual für `Base::m1`.

HINWEIS: Das obige Zusamenpiel entspricht dem grundlegenden Ansatz in der fortlaufenden Aufgabe mt den Klassen `Counter` und `OverflowCounter`. Wenn Ihnen letztere für verkettete Zählstufen "over-engineered" erscheint, bedenken Sie dass es sich nur um Beispiele handelt. Ein anderer Ansatz, der Zählstufen über Zeiger verkettet (die dann am Ende in einem nullptr auslaufen) finden Sie in `demo5`.

Kapitel 09 - Vererbung

Code F31 bis F33
http://coliru.stacked-crooked.com/a/ec64bfecd90552c1


Kapitel 08 - Spezielle Member-Funktionen (ohne Move-Semantik)

Erweitertes Beispiel zum `Singleton`

  • mit (optionalem) Objekt-Zähler und
  • alternativer C++-Implementierung

http://coliru.stacked-crooked.com/a/328de6f6379012f7


Kapitel 07 - Initialisierung von Objekten

Beispiel aus Wiederholung vom Morgen inkl. Konstruktor-Delegation und zugehörigen Tests:
http://coliru.stacked-crooked.com/a/890b918c63a3c312


Fortsetzung Kapitel 06 - Klassen

Code zum Ausprobieren als (Coliru-) Beispiel `StaticDemo`:
http://coliru.stacked-crooked.com/a/acfee374a1c583b2

Code zum Ausprobieren als (Coliru-) Beispiel zu `const` Member-Daten und -Funktionen:
http://coliru.stacked-crooked.com/a/2da7088caf782b70


Mittwochmorgen - Wiederholung zu Dienstag (inkl. kleinere Ergänzungen)

Überladen von Operatoren

Alles folgende bezieht sich auf dieses (Coliru-) Beispiel:
http://coliru.stacked-crooked.com/a/10569428f3dab7d4

Führen Sie ein kurzes Review durch, allein basierend auf dem, was Sie ohne weitere Kommentare im Quelltext erkennen können. Es handelt sich hier teilweise um Features, die noch nicht besprochen wurden. Versuchen Sie dennoch ein "educated guess", also eine Vermtung die Sie haben zu begründen. (Der lange und sehr ausführliche Kommentar zur "Robustheit" spielt noch keine Rolle, Sie können ihn lesen oder auch einfach übergehen.)

  • Welche Member-Variablen gibt es?
  • Lolalisieren Sie den Konstruktor und wie er die Member-Variablen initialisiert.
    HINWEIS: Die hier gezeigte Art der Initialisierung nennt sich "Member Initialization List" (oder kurz MI-List) und wurde gestern noch nicht vorgestellt. Wirkt sie dennoch verständlich?
  • Handelt es sich bei der Member-Funkion `invariants_check` um etwas, das JEDER Nutzer dieser Klasse aufrufen kann oder steht diese Funktion nur anderen Member-Funktion zur Verfügung?
  • Was könnte ihr Zweck sein und was könnte der Begriff "Invariante" allgemein gesprochen (in Bezug auf Klassen) bedeuten?
  • Lokalisiere Sie alle Member-Funktionen die in ihrem Namen das Wort `operator` verwenden.
  • Welche davon könnten für Inkrement und Dekrement:zuständig sein und wie könnten sich Prä-Inkrement bzw. -Dekrement von Post-Inkrement bzw. -Dekrement unterscheiden?
    HINWEIS: Dieser Unterschied ist nicht sonderlich intuitiv!
  • Welchen Zweck könnte die Member-Funktion mit dem Namen `operator int` erfüllen?

Schauen Sie sich zum Abschluss das Hauptprogramm an, welches eine sehr einfache Form "automatisierter Tests" mittels `assert` bereitstellt und begründen (oder widerlegen) Sie mit dessen Hilfe Ihre zuvor angestellten Vermutungen.

Zum Schluss nochmal (fast) derselbe Code, lediglich mit Kommentaren zu den Prä- und Postfix-Versionen der Inkrement/Dekrement Operatoren.

http://coliru.stacked-crooked.com/a/89de5d7f6d3db660


Kapitel 06 - Klassen

Übung zu Klassen letztem Abschnitt der Schulungsunterlage (4.1)


 

Kapitel 05 - Operatoren

!!!!!!!!! Setzen Sie fort mit dem Endresultat der Übung zu Kapitel 04 !!!!!!!

Definieren Sie die Größe von `my_array` ganz zu Beginn mit einem Präprozessor-Makro:

  • #define ARRAY_SIZE 10

Definieren Sie anschließend einen Ausgabe-Operator für ein gesamtes Array von `int` wie folgt:

  • std::ostream& operator<<(std::ostream& lhs, const int (&rhs)[ARRAY_SIZE]) {
        ... // TO-BE-DONE = von Ihnen zu erledigen
        return lhs;
    }
    HINWEIS: Es ist sehr viel einfacher, wenn Sie im "TO-BE-DONE"-Teil nun auf den bisherigen `std::for_each`-Aufruf verzichten und eine bereichsbasierte `for`-Schleife nehmen.
  • Im Hauptprogamm sollte zur Ausgabe damit folgendes ausreichend sein:
    std:.cout << my_array << std::endl;

Der momentane Zustand ist noch nicht "ideal", da der Ausgabe-Operator momentan nur für GESAMTE Arrays einer bestimmte Array-Größe mit Element-Typ `int` funkioniert.

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

Das abschließend zu verbessern erfordert Template-Programmierung ... (hier als Vorschau gezeigt, nicht die von Ihnen erwartete Lösung!)

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

 


Kapitel 04 - Kontrollstrukturen

Code F08 als (Coliru-) Beispiel:
http://coliru.stacked-crooked.com/a/2d8804a1c4f803c5

Erweitern Sie das Progamm wie folgt:

  • Ändern Sie die `printValue`-Funktion so ab, dass Sie alle alle Werte in eine Zeile druckt, mit Trennung durch Semikolo + Leerzeichen, und am Ende einen Zeilenvorschub vornimmt.
  • Verwenden Sie zur Initialisierung von `my_array` den Algorithmus `std::iota`
    https://en.cppreference.com/w/cpp/algorithm/iota
  • Definieren Sie den für `std::for_each` und `std::iota` benötigten "Endpunkt-Zeiger" in einer Hilfsvariablen nach `my_array` so dass automatisch auf eine Größenänderung reagiert wird.
    (Hinweis: sizeof `my_array / sizeof (int)` liefert die Anzahl der Elemente, berechnen Sie darauf bassieren aus der Adresse des ersten Elements PLUS diese Zahl einen Zeiger unmittelbar hinter das Ende des Arrays.)
  • Demonstrieren Sie nun, dass lediglich die Arraygröße (in eckigen Klammern) zu ändern ist und sich beides, Initialisierung und Ausgabe automatisch anpassen.
  • Bringen Sie das sortierte Array "durcheinander" mit `std::random_shuffle` und geben Sie es danach erneut aus.
    https://en.cppreference.com/w/cpp/algorithm/random_shuffle
    (Diese erneute Ausgabe soll nun auch für alle anderen Änderungen an `my_array` erfolgen, ohne dass das jeweils wieder gesagt wird.)
  • Sortieren Sie das "in Unordung gebrachte" `my_array` erneut mit `std::sort`
    https://en.cppreference.com/w/cpp/algorithm/sort
  • Wenden Sie nochmal `std::random_shuffle` an, diesmal aber nur auf die letzten fünf Elemente. (Voraussetzng ist natürlich, dass `my_array` nun mehr als 5 Elemente enthält - falls das nicht schon der Fall ist vergrößern Sie es entsprechend.)
  • Bestimmen Sie nun den Punkt bis zu dem das Array sortiert ist mit `std::is_sorted_until`
    https://en.cppreference.com/w/cpp/algorithm/is_sorted_until
    (Geben Sie dieses Mal zur Kontrolle den sortierten Anfang und das durcheinandergebrachte Ende in getrennten Zeilen aus.)
  • Sortieren Sie nun nur das unsortierte Ende mit `std::sort` NICHT GLEICH ALLES.
  • Verweden Sie abschließend `std::inplace_merge` um wieder das gesamte `my_array`in aufsteigend sortierte Reihenfolge zu bringen.
    https://en.cppreference.com/w/cpp/algorithm/inplace_merge

Dienstagmorgen - Wiederholung zu Montag (inkl. kleinere Ergänzungen)

Allgemein C vs. C++:

Ein bisschen etwas zum "Warmwerden".

Code zum Ausprobieren als (Coliru-) Beispiel:
http://coliru.stacked-crooked.com/a/93e2599f7257229b

  • Kompilieren Sie das Programm mit
    • gcc (also unverändert so wie im Beispiel)
    • clang (Verändern Sie also das Kommando)
    • g++ (behalten Sie wieder die Option `-xc` bei)
    • clang++ (auch hier dieOption `-xc` beibehalten)
  • Ermitteln Sie, in welchen C-Standards ist dieses Programm gültig ist.
    HINWEIS: Sie können für die nachfolgenden Tests jedes der obigen Compile-Kommandos verwenden, so lange Sie die Option `-xc` beibehalten;
    Einen konkreten C-Sprachstandard können Sie so auswählen:
    • -std=c89
    • -std=c90
    • -std=c99
    • -std=c11
    • -std=c18
  • Wie können Sie die Warnung vermeiden, die nun ausgegeben wird?
    HINWEIS: Warnungen sind immer eine Sache des Compilers und werden NICHT vom Sprachstandard vorgeschrieben.
  • Fügen Sie einen einen Kommentar im "C++ Stil" hinzu (der also mit zwei Schrägstrichen beginnt und sich bis zum Zeilenende erstreckt) und zeigen Sie, dass das Programm nun NICHT mehr in allen C-Standards gültig ist.
    • Konket: in welchen C-Sprachstandards geht das nicht?
  • Ermitteln Sie, in welchen C++-Standards das Programm gültig ist.
    HINWEIS: Welches der oigen Kommandos Sie zum Kompiieren verwenden ist egal, Sie müssen lediglich die Option `-xc` weglassen oder durch`-xc++` ersetzen.
    • (keine Option -std=...)
    • -std=c++98
    • -std=c++11
    • -std=c++14
    • -std=c++17
  • Falls Sie die Warnung in einem früheren Schritt durch Einfügen einer `return`-Anweisung in `main` beseitigt hatten, machen Sie dies nun wieder rückgängig.
    • Zeigen Sie, dass dies bei der Kompilierung mit C++ die Warnung unterbleibt. 
  • Was "sollte" man - auch wenn man bei der Ausgabe mit `printf` bleibt - an diesem Programm im Hinblick auf den Namensraum `std` ändern, damit es den Anforderungen des C++98 Standards genügt?
    HINWEIS: Bei den Compilern "gcc" und "clang" sind diese Maßnahmen keine absolut zwingende Erfordernis sondern eher eine Frage des "guten Stils".
  • Ersetzen Sie die Ausgabe
    • mit `printf` in C durch die C++-typische Form mit dem `<<` operator auf `std::cout` und
    • des Zeilenvorschubs mit `\n´ in C durch das C++-typische `std::endl`. 

Vordefinierte Makros:

ACHTUNG - und nur nochmal zur Erinnerung:
Alle Bezeichner, die, mit zwei Unterstrichzeichen beginnen, "gehören" der Implementierung des Compilers und könnten in diesem Rahmen prinzipiell zu beliebigen Zwecken verwendet werden.

Im C/C++-Standard festgelegt sind lediglich

  • __LINE__ = aktuelle Zeile des Quelltextes
  • __FILE__ = Name der kompilierten Quelltext-Datei
  • __FUNCTION__ = Name der aktuell kompilierten Funktion

Code zum Ausprobieren als (Coliru-) Beispiel:
http://coliru.stacked-crooked.com/a/f63362f312ee45f

Kann man statt der zweimaligen Verwendung von `std::cout` in zwei getrennte Anweisungen die gsamte Ausgabe auch in EINER Anweisung erzeugen?

  • Wie ändert sich die Ausgabe, wenn `foobar` im Namensraum `my` definiert ist? 
  • Wie unerscheidet __FUNCTION__ (überladene) gleichnamige Funktionen mit unterschiedlichen Argumentlisten?
  • Worin unterscheiden sich
    • __FUNCTION__ (im Sprachstandard vorgeschrieben)
    • __PRETTY_FUNCTION__ (Erweiterung von gcc/clang)

Untersuchen Sie ob 

  • __FUNCTION__ bzw. __PRETTY_FUNCTION__ auch außerhalb einer Funktionen verwendbar sind
  • und wenn ja, welchen Wert sie dort repräsentieren.

Hinweis 1:
zum Test könnten Sie globale Variable damit initialisieren, also
auto x = __FUNCTION__;
auto y = __PRETTY_FUNCTION__;
und deren Werte dann in `main` ausgeben.

Hinweis 2:
Testen Sie, ob sich g++ und clang++ in dieser Hinsicht gleich oder unterschiedlich verhalen.

Vordefinierte Makros zur Unerscheidung des Sprach-Standards;

Mit den folgenden Makros kann gegen bestimmte Versionen des C- oder C++-Sprachstandards getestet werden:

  • __STDC__
  • __STDC_VERSION__
  • __cplusplus

Code zum Ausprobieren als (Coliru-) Beispiel:
http://coliru.stacked-crooked.com/a/9774c3db7a31b4a5

  • Was könnte der Wert `201103L` des Makros `__cplusplus` hier bezeichnen?
  • Was sind die Defaults für eine C++-Kompilierung, wenn `-std=` NICHT angegeben ist ...
    • .. für g++ (bzw.gcc -xc++ ...) ?
    • ... für clang++ (bzw. clang -xc++ ...) ?
  • Testen Sie mit ein paar weiteren Kombinationen von `-x...` und `-std=...`

Eine kleine Vertiefung zum Verständnis von Referenzen:

Merkregel 1: Eine Referenz ist ein "Alias-Name" für irgend etwas, das "Speicherplatz hat

  • eine Variable (auch `const`-Variable)
  • eine array-Variable mit Index (in eckigen Klammern dahinter)
  • eine dereferenzierte Zeiger-Variable (mit einem Stern davor)
  • eine andere (bstehende) Referenz für das von ihr bezeichnete Objekt

Merkregel 2: Eine Referenz

  • wird "hinter den Kulissen" oft durch einen Zeiger implementiert,
  • für den "stillschwegend angenommen" wird, dass bei
    • dessen INITIALISIERUNG ein Adress-Operator (`&`) vor dem Initialisierungsausdruck steht,
    • jeder VERWENDUNG ein Derefenzierung erfolgt (so als ob ein `*` davor stünde),
    • und nicht - wie bei einem Zeiger - vor jeder Verwendung eine Prüfung erforderlich ist, ob "die Referenz nicht ins Leere geht" (in etwa analog zu einem ein Datei-Verweis für den die Datei gelöscht wurde, auf die der Verweis ursprünglich einmal zeigte).

Endresultat der Live-Demo als (Coliru-) Beispiel:
http://coliru.stacked-crooked.com/a/69c61306f9091f62


Kapitel 03 - Datentypen

Code F03 als (Coliru-) Beispiel:
http://coliru.stacked-crooked.com/a/8b8e68154bd832e7

mit einigen kleinen Modifikationen:
http://coliru.stacked-crooked.com/a/fbe3b5a8f65da4e4

Code F04 als (Coliru-) Beispiel:
http://coliru.stacked-crooked.com/a/3384ce84e7d521d5

mit einigen kleinen Modifikationen:
http://coliru.stacked-crooked.com/a/96f71587e54e3cff

Code F06

void doSomething(char * pc)
{
    cout << "char*" << endl;
}

void doSomething(int i)
{
    cout << "int" << endl;
}

void testNullPtr(void)
{
    doSomething("Test");
    doSomething(42);
}

AUFGABE: Kompilierbares Beispiel mit Online-Compiler erzeugen

Aufrufe testen mit

  • Ganzzahl
  • String-Literal
  • NULL
  • nullptr
  • (char*)0

Weitere Überladung für einen Pointer-Typ hinzufügen (z.B. int*) und erneut testen.

Lösung zur Diskussion (Version 1)

http://coliru.stacked-crooked.com/a/8346ee469086ce6c

Lösung zur Diskussion (Version 2)

http://coliru.stacked-crooked.com/a/62bae4b08ed734c4

Code F08

enum class TrafficLightState { RED, GREEN, YELLOW, RED_YELLOW };

enum class Colors{ PURPLE, RED, BLUE };

Aufgabe Unterschiede demonstrieren zu klassischem enum (ohne Zusatz class).

Lösung als Diskussionsbasis (mit Coliru):

http://coliru.stacked-crooked.com/a/94e8e0745419e5d1

Code F16 als (Coliru-) Beispiel

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

Aufgabe:

  • Erklären Sie (sich selbst) warum die momentan auskommentierten Zeilen zu einer Fehlermeldung führen
  • Demonstrieren Sie, dass `const` seine Position (links oder rechts) mit dem Typ WORAUF der Pointer zeigt wechseln kann.
  • Erklären Sie (sch selbst) warum es zu einer weiteren Fehlermeldung kommt, wenn Sie vor die ersten Variable (`c1`) ebenfalls `const` schreiben.

Code F18 als (Coliru-) Beispiel

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

Demonstrieren Sie mit diesem Beispiel dass ...

  • ... der Adress-Operator angewendet auf eine Referenz die Adresse derjenigen Variablen liefert, auf welche die Referenz "zeigt".
  • ... eine Referenz, welche mit einer (anderen) Referenz initialisiert wurde, NICHT einem "Doppelzeiger" entspricht.

Hinweis: Um eine Adressen mit `<<` auszugeben schreiben Sie eine `(void*)` Cast-Operation davor.

Ergänzen Sie das Beispiel nun wie folgt:

const char *greet = "hello, world";
const char &rgc = *greet;

  • Geht das überhaupt?
  • Wenn ja, worauf greift diese Referenz nun zu?
    Oder in anderen Worten: was erhalten Sie, wenn Sie `rgc` einfach mal ausgeben?

Last not least - was würde in den obigen Fällen passieren, wenn `greet` mit einem `nullptr` initialisiert worden wäre?

  • Machen Sie eine Vorhersage BEVOR Sie es ausprobieren!
  • Wenn Ihre Vorhersage die ist, dass es eine Fehlermeldung beim kompilieren gibt, geben Sie die Begründug dafür an.
  • Wenn Ihre Vorhersage die ist, dass das Programm abstürzen wird, dann sagen Sie auch WO genau das (wahrscheinlich) passieren wird

Kapitel 02 - Programmaufbau

Zusätzliches (Coliru-) Beispiel C-Ausgabe vs. C++ Ausgabe

http://coliru.stacked-crooked.com/a/66fb37549091f2e0

Code F15 für Copy&Paste in Online-Compiler

========================

void show(int i)
{
    cout << "int" << endl;
}

void show(double d)
{
    cout << "double" << endl;
}

int main(int argc, char* argv[])
{
    int i = 42;
    show(i);
    show(&i);
    cout << endl;
    return 0;
}

==========================

Etwas umgewandelt als leichter editierbares (Coliru-) Beispiel:

http://coliru.stacked-crooked.com/a/9e7e2763a531b311

 


Kapitel 01 - Einführung

ZIP-Archive ...

Weitere Links:

Online Referenzen zu C++

Online Compiler