C++11 - Seminar Programmierung Aufbau Dr.sc.nat. Michael J.M. Wagner, New Elements - Wagner Tech UG

Die Seite wird erstellt Paul Jäger
 
WEITER LESEN
C++11 - Seminar Programmierung Aufbau Dr.sc.nat. Michael J.M. Wagner, New Elements - Wagner Tech UG
C++11 - Seminar
                              Programmierung Aufbau

                    Dr.sc.nat. Michael J.M. Wagner, New Elements∗

                                     Revision 228

∗
    michael@wagnertech.de
C++11 - Seminar Programmierung Aufbau Dr.sc.nat. Michael J.M. Wagner, New Elements - Wagner Tech UG
Inhaltsverzeichnis
1 Einstieg in C++                                                                                                                                  4
  1.1 Ein- und Ausgabe von Dateien . . . . . . . . . . . . . . . . . . . . . . . . . . .                                                           6
  1.2 Objektorientierung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                                         8
  1.3 Vererbung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                                         11

2 Neuerungen in der Kernsprache                                                                                                                   14
  2.1 Verschiedenes (Teil 1) . . . . .    .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   15
  2.2 Objektdefinition . . . . . . . .    .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   16
  2.3 Lambda-Funktionen . . . . . .       .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   18
  2.4 Move vs. Copy . . . . . . . . .     .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   20
  2.5 Verschiedenes (Teil 2) . . . . .    .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   22

3 Templates                                                                                                                                       25
  3.1 Funktionstemplates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                                          25
  3.2 Klassentemplates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                                        26
  3.3 Templates der Standardbibliothek . . . . . . . . . . . . . . . . . . . . . . . . .                                                          26

4 C++11 Standardbibliothek                                                                                                                        29
  4.1 Neue Container . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                                          29
  4.2 Algorithmen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                                         32
  4.3 Neue Funktionalität . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .                                                         32

5 Multithreading                                                                                                                                  38

6 Quellen                                                                                                                                         39
C++11 - Seminar Programmierung Aufbau Dr.sc.nat. Michael J.M. Wagner, New Elements - Wagner Tech UG
IT-Schulungen.com
                                             Portfolio
        IT-Schulungen.com ist eines der führenden, herstellerunabhängigen Seminarportale von
        Schulungen rund um die Informationstechnologie (IT) und das IT-Management. Seit über 15
        Jahren ist IT-Schulungen.com eine anerkannte Anlaufstelle für viele Unternehmen und
        Behörden, wenn es um die Durchführung von DACH-weiten Schulungen geht.

        •   Applikationsserver /          •   ERP-Systeme               •   Open Source
            Middleware                    •   IT Management             •   Portale
        •   Business Intelligence         •   IT-Recht / Lizenzierung   •   SAP®
        •   Business-Skills und           •   ITIL                      •   Security
            Führung                       •   Mobile                    •   Serversysteme
        •   Cloud                         •   Multimedia                •   Softwareentwicklung
        •   CRM                           •   Office                    •   Systemmanagement
        •   Datenbanken
        •   eBusiness

www.IT-Schulungen.com                                              New Elements GmbH | IT-
                                                                   Schulungen.com

     Zertifizierungen & Partnerschaften

www.IT-Schulungen.com                                              New Elements GmbH | IT-
                                                                   Schulungen.com

                                                      3
C++11 - Seminar Programmierung Aufbau Dr.sc.nat. Michael J.M. Wagner, New Elements - Wagner Tech UG
Abbildung 1: C++-Geschichte2

1 Einstieg in C++1
Als Urväter von C++ gelten Smalltalk, die erste objektorientierte Programmiersprache und C,
die Spache für die Systemprogrammierung. C++ ist daher eine objektorietieret Sprache, die auch
für die Systemprogrammierung geeignet ist.
Die Geschichte von C++ startet in den späten 70-ern. Mit dem Hype der Objektorietierung,
der nach und nach aufkam, hasst alles „objektorieniert” zu sein. Der erste Titel war daher 1979
„C with classes”. 1983 wurde der Template-Mechanismus hinzugefügt und das Ganze hieß nun
C++.
Während des Hype der Objektorietierung wurde die Ansicht vertreten, dass allein die Einführung
der Objektorientierung zu wiederverwendbarer und wartbarer Software führt. Neue nützlich Klas-
sen werden entwickelt und gemeinsam genutzt. Aber was passierte: Nützliche Klassen wurden
für dasselbe Problem immer wieder auf’s Neue entwickelt. In den 90-ern existierte damit eine
Vielzahl von Klassen beispielsweise zur Behandlung von Zeichenketten. Jeder Compiler-Hersteller
lieferte seine eigene string -Klasse. Und diese Klassen waren nicht kompatibel. Daher wurde ein
Standardisierungsprozess gestartet. Die Schritte der Standardisierung zeigt Abbildung 1.

      • Annotated Reference Manual : Entwürfe der Standardisierung
      • 1998: ANSI-C++
         Dieser Standard enthält die wichtigen Klassenbibliotheken. Die meisten sind Template-
         Klassen. Daher wird dieser Standard oft STL (standard template library ) genannt.
      • 2003: Verbesserungen zu C++98
         Wichtigste Neuerung: auto_ptr, eine erste Klasse zur sicheren Freispeicherverwaltung.

 1
     Wolf: Kap. 1
 2
     Grimm: S. 3

                                                  4
• 2011: C++11
         2011 gab es keinen Hype der Objektorietierung mehr. Functional programming hielt nun
         Einzug in die Sprache. Was bedeutet dies? Functional programming vermeidet Schleifen.
         Wenn ein Algorithmus auf eine Menge Daten angewendet werden soll, wird der Algorithmus
         definiert und dem Compiler mitgeteilt, dass dieser Algorithmus auf jene Daten anzuwenden
         sei. Dieser Ansatz ermöglicht dem Compiler eine implizite Parallelisierung. Für eine einfache
         Formulierung von Algorithmen wurden die Lambda-Funktionen eingeführt. Eine Lambda-
         Funktion ist eine ad hoc-Definition einer Funktion.
      • 2014: C++14: Verbesserungen zu C++11
         Die Lambda-Finktionen erhalten neue Eigenschaften. In Kombination mit dem auto-Schlüsselwort
         können nun auch generische Lambda-Funktionen geschrieben werden.
      • 2017: C++17: Verbesserungen zu C++11
         Standardisierung der Parallelisierung

Ziele von C++ 113
      • Für den Einsteiger: einfacher zu lernen
      • Für den Profi: eine noch bessere Programmiersprache für die Systemprogrammierung
      • Multiparadigmen-Progammiersprache
            – prozedural, strukturiert (C)
            – objektorientiert, generisch (C++98)
            – funktional (C++11): Vermeidung von Schleifen, Zuweisungen, ermöglicht dem Com-
              piler eine Parallelisierung

Das erste C++-Programm4

Ein erstes einfaches Programm ist in Abbildung 2 abgebildet. Bevor es an die Praxis geht, sollen
folgende Spachelemente besprochen werden:

      • Blöcke
      • Ein- Ausgabeströme
         In der Sprache C++ gibt es keine Funktionen zur Ein- oder Ausgabe, die Bestandteile der
         Sprache selbst sind. Stattdessen wird die einfache Ein- und Ausgabe über die (objektorien-
         tierte) Streambibliothek  definiert:
            – std::cout: Standard-Ausgabestream
            – std::cerr: Standard-Fehlerausgabestream
            – std::cin: Standard-Eingabestream

 3
     Grimm: S. 7
 4
     Wolf: Kap. 2

                                                   5
# include < iostream >
     /*
       * Hauptprogramm
       */
     int main () {
        std :: cout  i ;
        return 0;
     }

                             Abbildung 2: Das erste Programm in C++

      • Bezeichner: Variablen, Funktionen, Klassen
      • Literale
      • Kommentare

 Aufgabe:
 Erstellen Sie in Ihrer Entwicklungsumgebung ein neues Projekt und bringen Sie ein einfaches
 „Hallo Welt”-Programm zum Laufen.

1.1 Ein- und Ausgabe von Dateien5

Zum Lesen und Schreiben von Dateien stehen die Klassen std::ifstream und std::ofstream zur
Verfügung.
Folgende Dateioperationen werden unterstützt:
      • Öffnen von Dateien
         # include < fstream >
         std :: ofstream file01 (" testdatei001 . dat ");
         std :: ifstream file02 (" testdatei002 . dat ");
         if ( ! file02 ) {
            std :: cerr
• Schließen von Dateien: data01.close();
         Im Allgemeinen ist es nicht nötig Dateien zu schließen, da dies mit dem Ende des Blocks
         automatisch geschieht.
      • Zeilenweises Lesen und Schreiben
         # include < fstream >
         using namespace std ;
         ifstream rStream (" datei . txt ");
         ofstream wStream (" out . txt ");
         string line ;
         while ( getline ( rStream , line )) {
            wStream  tokenize ( stringstream & ss , char token );
 6
     Wolf: Kap. 17.3.6

                                                  7
Diese Funktion liest immer wieder vom stream ss bis zum nächsten Auftreten von token oder
zum nächsten Zeilenumbruch und setzt die gelesenen Stücke zu einem vector (Array) aus Zei-
chenketten zusammen.
char toChar ( const string & str );

wandelt eine Zeichenkette in einen Charakter, indem die Zeichenkette in einen stringstream
geschrieben wird und das erste Zeichen wieder zurückgelesen wird.
int toInt ( const string & str );

wandelt eine Zeichenkette in eine Ganzzahl, indem die Zeichenkette in einen stringstream ge-
schrieben wird und alle Ziffern vom stream in eine Integervariable zurückgelesen werden.

 Aufgabe:
 Nehmen die Dateien util.h/cpp in Ihr Projekt auf und übersetzen Sie diese.

1.2 Objektorientierung

Klassen7

Klassendefinition:8
class Klassenname {
// Auf Elemente kann nur innerhalb einer Klasse
// zugegriffen werden
private :
typ daten1 ;
typ daten2 ;
...
// Zugriff von aussen auf die Elemente m " oglich
public :
typ funktionsname1 ( parameter );
typ funktionsname2 ( parameter );
...
};

Ein Beispiel findet sich in listings/011/automat1/automat.h9
Die Definition der Klassenmethoden erfolgt in der zugehörigen cpp-Datei:
listings/011/automat1/automat.cpp10

Greifen Klassenmethoden auf Datenelemente der Instanz zu, kann dies mit einem this-> verdeut-
licht werden:

 7
   Wolf:   Kap. 11
 8
   Wolf:   S. 238.
 9
   Wolf:   S. 239.
10
   Wolf:   S. 240.

                                               8
string Automat :: get_standort () const {
   return this - > standort ;
}
void Automat :: set_standort ( const string & standort ) {
   this - > standort = standort ;
}

Greifen Klassenmethoden nur lesend auf die Datenelemente der Instanz zu, kann dies mit const
versichert werden.
Instanzierung auf dem Stapel:
Klassenname Objekt ;
Klassenname Objekt {}; // C ++11

// Beispiel f " ur Klasse Automat :
Automat device01 ;
Automat device01 , device02 ;

Automat device01 {}; // C ++11
Automat device01 {} , device02 {}; // C ++11

Aufruf von Klassenmethoden (Punktoperator):
// Objekt der Klasse " Automat " erzeugen
Automat device {};
// Daten des Objektes " andern
device . set_geld (20000);
device . set_standort (" Augsburg , Fuggerweg 345");
// Inhalt des Objektes ausgeben
device . print ();

Die Instanzierung im Freispeicher erfolgt mit new:
Automat * aptr = new Automat (); // Instanzierung
aptr - > set_geld (20000);       // Verwendung
delete aptr ;                    // Speicherfreigabe

Da das delete oft nicht korrekt implementiert wird, gibt es seit 2003 den auto_ptr, der mit
C++11 durch den unique_ptr ersetzt wurde.
# include < memory > // f " ur std :: unique_ptr
...
// Speicher f " ur ein neues Objekt anfordern
std :: unique_ptr < Automat > device_ptr ( new Automat {});
std :: unique_ptr < Automat > device_ptr = make_unique < Automat >(); // Alternative
auto device_ptr = make_unique < Automat >(); // Alternative mit make_unique / auto
device_ptr - > set_standort (" Mainz , Hauptstrasse 1");
device_ptr - > print ();

Weitere Funktionen von unique_ptr:
   • *device_ptr: Das dereferenzierte Objekt
   • device_ptr.get(): Der Zeiger selbst. Die Verantwortung bleibt aber beim device_ptr.
   • device_ptr.release(): Der Zeiger selbst. Die Verantwortung geht auf den Empfänger über,
     device_ptr selbst wird leer.

                                                9
• device_ptr.reset(p): device_ptr übernimmt den neuen Zeiger p. Falls bereits in device_ptr
     ein Zeiger gespeichert war, wird das zugehörige Objekt gelöscht.

  Ergänzen Sie die Bibliotheksanwendung
     • Erstellen Sie eine Klasse Medium mit den entsprechenden Bestandteilen und Defaultwer-
       ten. In den objektorientierten Sprachen werden Klassen üblicherweise in gleichnamige
       Dateien (Medium.cpp/.h) gelegt.
     • Schreiben Sie zu jedem Datenelement einen Getter (z.B. string getSignatur() const;)
     • Ergänzen Sie das Modul Medienverwaltung um eine Funktion
       int addMedium(const Medium& medium), die die bereits bestehende Methode verwendet.

     • Ergänzen Sie das Hauptprogramm um die Instanzierung eines Mediums mit seinen
       Defaultwerten auf dem Stapel und einen Aufruf dieser Methode.

Konstruktoren und Destruktoren

Konstruktoren sind Funktionen, die bei der Instanzierung einer Klasse, unabhängig davon, wo sie
instanziert wird, ablaufen. Sie dienen meist der Initialisierung. Konstruktoren können Parameter
aufnehmen. Standardmäßig steht der Default-Konstuktor (ohne Parameter) zur Verfügung. Dieser
wird aber ausgeblendet, sobald ein spezieller Konstruktor definiert wird. In C++11 lässt sich dieser
mit MyClass() = default; wieder einblenden.
Deklaration von Konstruktoren: [listings/011/automat2/automat.h]
Definition von Konstruktoren:
Klassenname :: Klassenname ( )
: data1 { wert } , data2 { wert } , dataN { wert } {
  // Konstruktionsk " orper mit Anweisungen
}

Sollen, wie im gezeigten Beispiel, im Konstruktor keine expliziten Prüfungen durchgeführt werden,
wenn der Funktionskörper also leer ist, dann empfiehlt es sich, den Konstruktor inline in der Hea-
derdatei zu definieren. Dazu lässt sich seit C++11 auch die Konstruktor-Delegation verwedenden
[listings/011/automat2/delegation/automat.h].
Es gibt Klassenelemente mit speziellen Signaturen, die der Compiler in bestimmten Situatio-
nen heranzieht. Grundsätzlich gilt hier ein „alles oder nichts”. Ist spezieller Code zum Kopieren
oder Löschen eines Objekts nötig, müssen alle drei (mit C++11 fünf) Elemente berücksichtigt
werden:
   • Kopierkonstruktor: Class(const Class&)
   • Zuweisungsoperator: Class& operator=(const Class&)
   • Destruktor: ~Class()
   • Move-Konstruktor (C++11): Class(Class&&)
   • Move-Zuweisung (C++11): Class& operator=(Class&&)

                                                10
In modernem C++-Code, der die Möglichkeiten der Standardlibrary nützt, sollte möglichst auf
die Verwendung dieser Sprachelemente verzichtet werden (rule of zero 11 ):
     • Verwenden Sie für größere Datenmengen fertige Container der Standardbibliothek.
     • Benutzen Sie die neuen klugen Zeiger.

 Ergänzen Sie die Bibliotheksanwendung:
      • Ergänzen Sie die Klasse Medium um einen Konstruktor, der die einzelnen Bestandteile
        als Parameter übernimmt.
      • Verwenden Sie im Hauptprogramm diesen Konstruktor.

1.3 Vererbung12

Motivation: Gemeinsamkeiten nur einmal implementieren: s. Abb. 3.

                              Abbildung 3: Vererbungsbeziehung13

Definition der Mutterklasse: [listings/014/vererbung001/person.h]
Ableitung von Kunde: [listings/014/vererbung001/kunde.h]
In Kunde wird das ”Mehr” des Kunden gegenüber der Person implementiert. Alle Eigenschaften
von Person erbt Kunde.
Wie zu sehen ist, enthalten sowohl Person als auch Kunde eine Funktion mit der Signatur
void print() const.

11
   Wolf: S. 291f.
12
   Wolf: Kap. 15
13
   Wolf: S. 346

                                               11
Dies nennt man Überschreiben von Methoden. Auf die überschriebene Methode der Mutterklasse
kann direkt aus der Kindklasse zugegriffen werden:
void print () const {
   std :: cout
Aufgabe:
 Ergänzen Sie die Bibliotheksanwendung um die Dateien MediumHira.h/.cpp:
     • Leiten Sie von der Klasse MediumBase die Klassen Buch und CD ab.
     • Verteilen Sie die Attribute sinnvoll auf die Klassen.
     • Ergänzen Sie jede Klasse um eine Methode string format() const, die den Datensatz
       als Zeichenkette in dem Format zurückgibt, wie er in der Eingabedatei erwartet wird.
       Für die Umwandlung von int in string gibt es die std::to_string-Funktion.
     • Für die Klasse MediumBase implementieren Sie eine Dummy-Ausgabe.
     • Testen Sie die format-Funktion für alle drei Klassen im Hauptprogramm.

Polymorphismus

Zur Motivation dieses Kapitels implementieren Sie diese Aufgabe:

 Aufgabe:
 Ergänzen Sie die Bibliotheksanwendung:
     • Ergänzen Sie die Medienverwalatung um eine Funktion
       int addMedium(const MediumBase& medium),

       die die neue format()-Methode zum Schreiben in die Datei verwendet. Auf die Dupli-
       katsprüfung kann erst mal verzichtet werden.
     • Verwenden Sie die Methode im Hauptprogramm, indem Sie einmal ein Buch, einmal
       eine CD übergeben.

Sie werden sehen, dass in der Datei nur immer der Dummy-Eintrag für ein Medium erscheint,
obwohl Sie im Hauptptogramm konkrete Instanzen implementiert haben. An dieser Stelle wird nun
ein Mechanismus benötigt, der zur Laufzeit die tatsächliche Klasse einer Instanz bestimmt und
danach die passende Implentierung auswählt. Dieser Mechanismus nennt sich Polymorphismus
(Vielgesichtigkeit).
Das polymorphe Überschreiben von Methoden erfolgt duch die modifier virtual in der Basisklasse
und override in den abgeleiteten Klassen [listings/014/virtual02/main.cpp].

 Aufgabe:
 Ergänzen Sie die Bibliotheksanwendung:
     • Implementieren Sie die format()-Methode polymorph.
     • Bringen Sie die neue addMedium-Methode zum Laufen.

                                               13
Abstrakte Klassen und Methoden

Die Dummy-Implementierung in MediumBase macht nicht viel Sinn, da ja Instanzen von MediumBase
auch nicht viel Sinn machen. MediumBase kapselt ja nur die Gemeinsamkeiten von Buch und CD.
Die Gemeinsamkeit bezüglich der format()-Methode ist, dass sie in den abgeleiteten Klassen
vorhanden ist. Die reine Existenz lässt sich mit einer Deklaration virtual string format() = 0;
beschreiben. Es wird dem Compiler gesagt, dass die abgeleiteten Klassen diese Methode im-
plementieren müssen. Als Konsequenz verhinder der Compiler, dass Instanten von MediumBase
gebildet werden. Die format()-Methode in MediumBase nennt sich abstrakte Methode.
Eine Klasse, die mindestens eine abstrakte Methode hat, nennt sich abstrakte Klasse 15 .

 Aufgabe:
 Ändern Sie MediumBase zu einer abstrakten Klasse ab.

 Aufgabe zu unique_ptr:
        • Ergänzen Sie die Definition der Klasse MediumBase um eine statische Methode
          (static Medium* createMedium(signatur, autor, ...)), die eine Instanz von Buch/CD im
          Freispeicher erzeugt. Innerhalb dieser Methode soll die Instanz in einem unique_ptr ge-
          halten werden.
        • Rufen Sie die Funktion im Hauptprogramm auf. Halten Sie dort die Instanz gleichfalls
          in einem unique_ptr und übergeben Sie die Instanz an addMedium.

2 Neuerungen in der Kernsprache
Anmerkung: Wenn ein Codebeispiel mit Ihrem Compiler nicht funktioniert, können Sie den Wand-
box16 -Compiler verwenden.

15
     Wolf: S. 364
16
     https://wandbox.org/

                                                 14
2.1 Verschiedenes (Teil 1)

Range-basierte For-Schleife und Typableitung

Aus vielen modernen Programmiersprachen bekannt (foreach)
      • für C-Array
      • für Container (u.a. vector, map, initializer_list)
      • for (typ var : container_var)
Der Compiler weiß an vielen Stellen, was für ein Typ verwendet werden muss.
      • Typisierung mit auto
         auto i = 5;

         for (auto var : container_var)

      • Typisierung mit decltype
         int b ;
         decltype ( b ) a ;

auto steht immer für den Wert-Typ (keine Referenz). decltype hingegen liefert auch Referenz-
typen. Mit C++14 steht das Konstrukt decltype(auto) zur Verfügung, das wie auto verwendet
wird und auch Referenztypen liefert:17
auto i              =         5;       //   int
int & iref          =         i;       //   int &
auto c              =         iref ;   //   int
auto & d            =         i;       //   int &
decltype ( iref ) e =         i;       //   int &
decltype ( auto ) f =         iref ;   //   int &

Mit C++11 ist es damit sehr einfach geworden über diese Container zu iterieren.
for ( auto & mapIt : phonebook )
        std :: cout
nullptr

Bisher wurde NULL für einen Zeiger benutzt, der auf keinen gültigen Speicher zeigt. NULL ist aber
als Alias für die Zahl 0 definiert. Daher ist NULL vom Typ int. Mit C++11 war es das Ziel zwischen
Zeigern und Zahlen unterscheiden zu können. nullptr wurde daher im Sprachkern definiert und
ist vom Typ void*.

Automatischer Rückgabetyp

Bei der Deklaration/Definition von Funktionen kann auch das Schlüsselwort auto für den Typ
verwendet werden. In diesem Fall bestimmt das erste return-Statement den Rückgabetyp.19
auto Correct ( int i ) {
  if ( i == 1)
    return i ;                         // return type deduced as int
  else
    return Correct (i -1)+ i ;         // ok to call it now
}

2.2 Objektdefinition

Diverse Neuerungen in der Klassendefinition

     • Die Initialisierer-Liste: ein neuer Container.
          – Verwendung in Konstruktoren
          – Deklaration: MyClass(std::initializer_list data);
          – Verwendung: MyClass myClass{ele1, ele2, ele3};
          – Beispiel: [TourDeC++11/initializerListConstructor.cpp]20
     • Delegation von Konstruktoren:
       Mit der „:”-Syntax kann ein anderer Konstruktor aufgerufen werden:
       .[TourDeC++11/delegatingConstructor.cpp]21
     • Vererbung von Konstruktoren: using Base::Base;
       .[TourDeC++11/inheritingConstructor.cpp]22

19
   https://en.wikipedia.org/wiki/C%2B%2B14#Function_return_type_deduction (24.8.2018)
20
   Grimm: S. 20.
21
   Grimm: S. 22.
22
   Grimm: S. 23f.

                                                  16
• Direktes Initialisieren von Klassenelementen: int x = 5; (wie bei Java)
       .[TourDeC++11/classMemeberInitializer.cpp]23
     • Steuerung der compilergenerierten Konstruktoren+Operatoren: default, delete
       .[TourDeC++11/defaultedDeletedMethods.cpp]24
     • Steuerung der Vererbung von Methoden: override, final (wie bei Visual-C++/C#)

 Aufgabe:
 Ergänzen Sie das Beispiel initializerListConstructor.cpp in der Weise, dass Sie folgende
 Konstruktoren zur Verfügung haben:
      • Standardkonstruktor
      • Initialisiererliste für Integer
      • Konstruktor mit zwei Integer
 Prüfen Sie nun, welche Instanzierung welchen Konstruktor aufruft:
      • Klasse k1;
      • Klasse k2{};
      • Klasse k3(3,4);
      • Klasse k4{3,4};
 Entfernen Sie nun nach und nach die Konstruktoren, damit Sie erkennen, welche dieser In-
 stanzierungen auch auf andere Implementierungen ausweichen können.

Vereinheitlichte Initialisierung

C++ 11 hat eine einheitliche Initialisierungs-Syntax für
     • Strukturen
       Klassen, die ausschließlich public members haben und keine Konstruktoren aufweisen.
     • C-Arrays
     • Container
     • Im Konstruktor
     • Arrays auf dem Heap

 Aufgabe:
 Compilieren und Analysieren Sie das Beispiel Kernsprache/uniformInitialisation.cpp25

23
   Grimm: S. 24f.
24
   Grimm: S. 27f.
25
   Grimm: S. 120.

                                                17
2.3 Lambda-Funktionen

   • Funktionen ohne Namen (anonym)
   • Lassen sich dort einsetzen, wo Funktionszeiger oder Funktoren (ausführbare Objekte, das
     sind Instanzen von Klassen, die den ()-Operator implementieren) vorkommen.

Syntax:
[*1](*2){*3}
*1: Bindung an den lokalen Kontext
  []: Keine Bindung
  [=]: Alle Werte werden kopiert ( Schnappschuss ).
  [&]: Alle Werte werden referenziert .
*2: Lau fzeitpar ameter
*3: Implementierung

  Aufgabe:
     • Compilieren und analysieren Sie das Beispiel anhang_a/lambda.cpp
     • Ergänzen Sie das Beispiel um einen Anufruf mit einem Funktionszeiger und einem
       Funktor.

Bindung an den Kontext

Sollen Variablen aus dem aktuellen Kontext in die Lambdafunktion hineingenommen werden,
müssen diese in den eckigen Klammern angegeben werden. Mit einem vorangestellen & wird die
Variable als Referenz übergeben, sonst wird sie kopiert. Sollen alle Variablen als Referenz gebunden
werden, steht & allein in der eckigen Klammer, ein = allein kopiert alle Variablen.

  Aufgabe:
  Ergänzen Sie Die Bibliotheksanwengung um eine show_sort_title(), die die Medien nach Titel
  sortiert ausgibt. Die Funktion soll wie show_sort_author() aus Kap. 3.3 aufgebaut sein, mit
  dem Unterschied, dass statt dem Funktor eine Lambda-Funktion verwendet wird. Der Vektor
  wird in der Funktion show_sort_title() definiert und in den Kontext der Lambda-Funktion
  gebunden.

Ab C++14 können auch Variablen explizit aus dem Kontext vorbelegt werden, dabei kann auch
die move-Funktion verwendet werden:
auto lambda = [value = std::move(ptr)] {return *value;};

                                                18
Aufgabe:
  Ändern Sie lambda.cpp so ab, dass der Divisor variabel ist. Der Divisor soll nun
       • durch eine Variable aus dem Kontext bestimmt werden,
       • durch Zuweisung einer Zahl belegt werden.

Generische Lambdas (C++14)26

In C++11 mussten die Typen der Parameter deklariert werden. Mit C++14 können auch die
Parameter mit auto allgemein gehalten werden.
auto lambda = []( auto x , auto y ) { return x + y ;};

Dieser Code ist äquivalent zu einem ausführbaren Klassentemplate (Funktor):
struct unnamed_lambda
{
   template < typename T , typename U >
     auto operator ()( T x , U y ) const { return x + y ;}
};
auto lambda = unnamed_lambda {};

Generische Lambdas sind innerhalb generischem Code, also bei der Implementierung von Tem-
plates nützlich. Auch der Umgang mit variant wird dadurch einfacher (s. Kap. 4.3).

bind und function

bind macht aus bestehenden Funktionsobjekten neue, indem es Argumente an den aktuellen
Kontext bindet und Platzhalter erklärt.
function   kann einem Funktionsobjekt einen Namen zuweisen, sodass ein aufrufbares Objekt ent-
steht.
Grimms Kommentar zu diesem Thema in Abb. 5.

  Aufgabe:

       • Compilieren und Analysieren Sie folgendes Beispiel28 : 20-29 (bindAndFunction.cpp)
       • Stellen Sie das Beispiel auf auto/Lambdafunktion um.

26
   https://en.wikipedia.org/wiki/C%2B%2B14 (27.8.2018)
27
   Grimm: S. 456
28
   Grimm

                                                  19
Abbildung 5: bind und function27

2.4 Move vs. Copy

Problem: C++ kopiert viel zu viel:
      • Objekte werden in die Container kopiert
         Hat das Objekt nach dem Kopiervorgang noch eine Verwendung?
      • Anders in Java, C#, Visual C++:
         Referenzen werden kopiert + Garbage-Collector
Move ermöglicht performanteres Übertragen von Objekten: Abb. 6.

      • Wann wird move verwendet?
         explizit: a = std::move(b);
         implizit: Rechts steht ein Rvalue

29
     Grimm: S. 32

                                                  20
Abbildung 6: Move vs. Copy29

      • Was ist ein Rvalue?
            – Ein Ausdruck, der nur rechts vom Gleichheitszeichen stehen darf
            – Ausdruck ohne Name: a = MyObject();

Die Syntax für Move-Konstruktor und Move-Zuweisung:
MyClass ( MyClass &&) { ... }
MyClass & operator =( MyClass &&) { ... }

Zu beachten sind dabei folgende Regeln:
      • Regel der großen Fünf : Wenn einer der großen Fünf implementiert wird, müssen alle fünf
        implementiert werden.
      • Nullregel : Es ist besser keinen der großen Fünf zu implementieren30 .
Sollen einzelne der implizit durch den Compiler generierten Operationen verboten werden, kann
dies mit delete erfolgen.
class Simple {
private :
   int * daten { nullptr }; // Roher Zeiger !!!
   Simple ( int n ) : daten { new int [ n ] } {}
   ~ Simple () { delete [] daten ; }
   Simple ( const Simple &) = delete ; // keine Kopie
   Simple & operator =( const Simple &)= delete ; // keine Zuweisung
   Simple ( Simple &&) = delete ; // kein Verschieben
   Simple & operator =( Simple &&)= delete ; // keine V ers ch ie bzu we is ung
};
30
     Wolf: 291.

                                                  21
Aufgabe:
 Compilieren und Analysieren Sie Move.cpp. 5x wird ein großes Array kopiert. Das soll im
 Folgenden verhindert werden:
        • Ergänzen Sie in der Klassendefinition den Move-Konstruktor und die Move-Zuweisung.
           Jetzt sollten es nur noch zwei/drei (je nach Compiler) Kopiervorgänge sein.
        • An einer Stelle können Sie durch Einfügen eines std::move ein weiteres Kopieren ver-
          hindern.

Die Möglichkeit Freispeicher durch eine andere Instanz übernehmen zu lassen, verhindert zwar
oftmals unnötiges Kopieren. Schöner wäre es aber, Instanzen direkt im Speicher des Contai-
ners instanzieren zu können. Die neuen emplace-Funktionen der Standard-Container ermöglichen
dies. Bei einem emplace werden die Parameter so angegeben, wie sie für einen Konstruktoraufruf
benötigt werden: Beispiel für vector31

 Aufgabe:
 Ändern Sie das Move-Beispiel so ab, dass durch emplace_back ein move in den vector ver-
 mieden wird.

2.5 Verschiedenes (Teil 2)

Definition von Typen

Ein weiteres neues Sprachelement ist using als Ersatz von typedef:
typedef struct my_struct my_type ;
// ist gleichbedeutend mit
using my_type = struct my_struct ;

Mit using lassen sich Templates (teilweise) binden32 :
template < typename T , int Line , int Column >
class Matrix ;

template < typename T , int Line >
using Square = Matrix ;

template < typename T , int Line >
using Vector = Matrix ;

31
     http://www.cplusplus.com/reference/vector/vector/emplace/
32
     Grimm: S. 186

                                                     22
Typsichere Aufzählungen

enum class:

     • Typsicher, da nicht implizit zu int konvertierbar
     • Keine namespace pollution
     • ABER: Keine Rückkonvertierung zum Namen möglich
[Kernsprache/enum.cpp]33

Neue Literale

     • Raw Strings34
       R"Trenner(raw string)Trenner"
       Trenner: beliebige (auch leere) Zeichenkette

     • Benutzerdefinierte Literale
       Literale der Form: wert_einheit (25.6_km, 2345_s, 2.2e24_kg)
       Beispiel: [userDefinedLiterale.cpp]35
     • Binäre Literale: Mit C++14 lassen sich Literale der Form 0b001010011 schreiben.
     • Digit Seperator: Zur Lesbarkeit ist es möglich das einfache Hochkomma einzufügen:
       0b0’0101’0011, 123’345’678

constexpr

Mit C++11 wurde constexpr als Verbesserung von #define und const eingeführt: Typssicher wie
const, Auswertung zur Compilezeit wie #define:
constexpr double e a r t h _ g r a v i t a t i o n a l _ a c c e l e r a t i o n = 9.8;

constexpr int get_five () { return 5;}
int some_value [ get_five () + 7]; // Create an array of 12 integers . Valid C ++11

Mit C++17 kommt constexpr-if hinzu:
if constexpr ( a n y _ c o m p i l e _ t i m e _ b o o l e a n _ e x p r e s s i o n )
{
  // this is only compiled , if a n y _ c o m p i l e _ t i m e _ b o o l e a n _ e x p r e s s i o n is true
  int i = 5;
}
else
{
  double i = 5.;
}

33
   Grimm: S. 202f.
34
   Grimm: S. 205f.
35
   Grimm: S. 213f.

                                                        23
Dies findet Anwendung mit den neuen Möglichkeiten Typinformationen zur Compilezeit abzufra-
gen.

Variadic Templates

Templates mit variabler Zahl von Parametern
   • Element zur funktionalen Programmierung
     template < typename ... Args >
     function ( Args ... args )

   • args ist dabei ein „Parameter Pack”
   • args... ist die entpackte Parameterliste
# include   < iostream >
# include   < vector >
# include   < algorithm >
# include   < typeinfo >

template < typename T = int >
void mischmasch ( T val =0 L ) {
  std :: cout . width (3);
  std :: cout
Die Funktion soll dann folgendermaßen aufgerufen werden:
     string signatur , autor , titel ;
     char typ ;
     int seitenzahl , spieldauer ;
     stringstream ss (" A01 , Heinz Autor , Der Titel ,B ,125 ,0");
     bool ok = split ( ss , ’ , ’ , signatur , autor , titel , typ , seitenzahl , spieldauer );

3 Templates36
Templates bieten eine Form der generischen Implementierung. Man unterscheidet Funktionstem-
plates von Klassentemplates.

3.1 Funktionstemplates

Motivation: Identischer Code, der sich nur in einigen Typen unterscheidet
Beispiel: [listings/015/Templates001/main.cpp].
Implementierung: [listings/015/Templates002/main.cpp]
Umgang mit Ausnahmen, also Typen, die eine spezielle Implementierung erfordern:
std :: string str1 {" Hallo "} , str2 {" Welt "};
std :: cout
3.2 Klassentemplates

Definition: [listings/015/SimpleCTemp/CTemp.h]
Instanzieren:
CTemp < double > dval ;
CTemp < std :: string > sval ;

Für Templates wird erst dann Code gebildet und compiliert, wenn es für einen bestimmten Typ
instanziert wird. Für Typen, die oft vorkommen, kann ein Template auch „auf Vorrat” instanziert
werden:
template < class T >
class Y // template definition
{
   void mf () { }
};
template class Y < char * >;   // explicit instantiation

Damit dieses Template dann aber nicht erneut an anderen Stellen compilert wird wird, muss es
dort mit
extern template class Y < char * >;

bekannt gegeben werden. Dieses Vorgehen rentiert sich aber nur bei entsprechend großen Tem-
plates. Beim Linken wir überflüssiger Code (z.B. mehrfache Instanzierungen eines Templates für
denselben Typ) entfernt.37

3.3 Templates der Standardbibliothek

Container der Standardbibliothek38 →vector, map, array (C++11)
Eine typische Verwendung von std::map finden Sie in Abb. 7.
Um über Container zu iterieren gibt es das Iteratorkonzept. Der Iterator ist ein spezieller Zeiger auf
ein Element des Containers. Mit der Methode begin() erhält man den Iterator, der auf das erste
Element des Containers zeigt, end() ist nach dem letzen Element positioniert. Der ++-Operator
schiebt den Iterator auf das nächste Element. Eine Schleife über alle Elemente lässt sich daher
so formulieren:
std :: map < std :: string , std :: string > phonebook ;
// fill phonebook with data
std :: map < std :: string , std :: string >:: iterator mapIt ;
for ( mapIt = phonebook . begin (); mapIt != phonebook . end (); mapIt ++)
          std :: cout  first
# include 
// definition
std :: map < std :: string , int > mymap ;
// Element hinzuf " ugen / " uberschreiben
mymap [" eins "] = 1;
// Element abfragen . Wenn nicht existent , wird es eingef " ugt .
int i = mymap [" eins "];
// Element abfragen . Wenn nicht existent , wird eine Exception geworfen .
int j = mymap . at (" drei ");
// Abzahl der Elemente in der map
int a = mymap . size ();
// Anzahl der Elemente mit einem bestimmten Schl " ussel ( kann 0/1 sein )
int k = mymap . count (" zwei ");

                          Abbildung 7: Verwendung von std::map

Aufgabe:
Ergänzen Sie die Bibliotheksanwendung. Um die Datei nicht jedes mal auf’s Neue lesen zu
müssen, sollen die Daten in einer std::map39 abgelegt werden. Dazu bekommt die Medienver-
waltung jetzt interne Daten, wird vom Funktionsmodul zur Klasse.
   • Legen Sie die Klasse MedienverwaltungClass mit den internen Daten
      std::map medium_map;    an.
   • Schreiben Sie eine Methode load(), die die Datei einliest und das Verzeichnis füllt.
     Als Schlüssel soll dabei die Signatur dienen, als Wert eine Instanz von Medium. Diese
     Funktion soll im Konstruktor aufgerufen werden.
      Nehmen Sie die Implementierung von bool isSignatureInFile(const string& sig) als
      Vorlage.
   • Schreiben Sie eine Methode bool checkDuplicate(const string& signatur), die prüft,
     ob der gegebene Schlüssel in der map vorhanden ist.
   • Schreiben Sie eine Methode void show(), die über das Verzeichnis iteriert und al-
     le gespeicherten Medien ausgibt. Beachten Sie, dass das Element einer std::map ein
     Schlüssel-Wert-Paar ist. Auf den Schlüssel wird mit .first, auf den Wert mit .second
     zugegriffen.
   • Schreiben Sie eine Methode int addMedium(const Medium& medium), die checkDuplicate
     aufruft und das Medium an die Datei anhängt, danach das Verzeichnis mit load aktua-
     lisiert.
   • Verwenden Sie den neuen Code im Hauptprogramm.
Da die Medium-Instanzen direkt in die Map kopiert werden, funktioniert dieses Konstrukt nicht
mit Instanzen einer Klassenhierarchie. In diesem Fall müssen die Instanzen im Freispeicher
liegen und in der Map die Zeiger verwaltet werden.

                                             27
• Legen Sie in MedienverwaltungClass eine map an und
       füllen Sie diese gleichfalls in der load-Methode. Zur Erzeugung der Instanz kann die
       Fabrikfunktion createMedium verwendet werden.
     • Ergänzen Sie die Methode show(), so dass auch diese Map ausgegeben wird.

Algorithmen der Standardbibliothek

Algorithmen der Standardbibliothek40 → for_each(), find(), find_if(), copy(), count(),
count_if(), sort()

Viele dieser Algorithmen benötigen eine Funktion als Parameter. Die kann eine „normale” Funk-
tion, aber auch ein Funktionsobjekt sein. Ein Funktionsobjekt (Funktor) ist eine Klasse, die den
()-Operator implementiert:
class MyFunctor {
private :
          int internal ;
public :
          MyFunctor ( int i ) : internal ( i ) {}
          bool operator ()( int i ) { return i < internal ; }
}

 Aufgabe:
 Ergänzen Sie die Bibliotheksanwendung um eine Funktion show_sort_author(), die die Medien
 nach autor sortiert ausgibt:
     • show_sort_author() kopiert alle Instanzen aus der Medium-Map in einen Vektor. Dazu
       wird ein Funktor angelegt, der einen Vektor enthält. Zum Kopieren wird die for_each()-
       Funktion verwendet, die für jeden Map-Eintrag den Funktor aufruft, der wiederum die
       Medium-Instanz (.second) dem internen Vektor anhängt. Die for_each()-Funktion gibt
       den veränderten Funktor nach der Ausführung als Rückgabewert zurück. Nun kann der
       Vektor aus dem Funktor ausgelesen werden.
     • Schreiben Sie eine Funktion
        bool comp_media(const Medium& m1, const Medium& m2)
        die true zurück gibt, falls author von m2 größer ist.
     • Sortieren Sie den Vector mit der sort-Funktion.
     • Geben Sie den Vektor mit Hilfe der copy-Funktion aus. Das Kopierziel ist ein output
       stream iterator41

39
   Nützliche Informationen zur Standardlibrary findet man unter http://cplusplus.com
40
   http://www.cplusplus.com/reference/algorithm/
41
   http://www.cplusplus.com/reference/iterator/ostream_iterator/algorithm/ (3.5.2019)

                                                    28
4 C++11 Standardbibliothek

4.1 Neue Container

Tupel

tuple   ist die Verallgemeinerung von pair. Es nimmt mehrere Daten verschienenen Typs auf.
Für tuple existieren diverse Hilfsfunktionen in der Standardbibliothek:
.[DieStandardbibliothek/helperTuple.cpp]42
      • Z. 10: make_tuple erzeugt ein Tupel aus einzelnen Werten.
      • Z. 23: Zusammen mit dem Referenzwrapper ref/cref wird ein Tupel aus Referenzen er-
        zeugt.
      • Z. 50: tie erzeugt ein Tupel aus Referenzen für alle Werte. Dies ist eine einfache Möglichkeit
        die Werte eines Tupels Variablen zuzuweisen.

 Aufgabe43 :
 Ergänzen Sie folgende Funktion, so dass sie vier heterogene Typen zurück gibt und geben Sie
 die Werte im Hauptprogramm aus:
     ??? returnFourValues () {
       int a = 5;
       double b = 10.2;
       std :: string c = " test ";
       bool d = true ;
       return ??? ;
     }

 Der klassische C++-Weg bestand darin, eine Struktur zu definieren, die die vier Typen bindet,
 und diese Struktur als Rückgabewert zu verwenden. In C++11 gibt es einen einfacheren und
 besseren Weg.

Mit C++17 soll die Möglichkeit kommen, das Rückgabetupel direkt einzelnen Variablen zuzuwei-
sen, die im selben Schritt auch deklariert werden:
auto [a, b] = getTwoReturnValues();44

Anmerkung: Der Referenz-Wrapper ist auch bei der Lösung des klassischen Problems hilfreich,
dass Container keine Referenzen enthalten können. Der Container kann den Referenz-Wrapper
als Element haben, dieser muss aber mit emplace direkt im Container erzeugt und belegt werden.
[TourDeC++11/referenceWrapper.cpp]

42
   Grimm: S. 417.
43
   Grimm: Aufg. 20-7, S. 422. Lösung: fourReturnValues.cpp
44
   https://en.wikipedia.org/wiki/C%2B%2B17 (27.8.2018)

                                                    29
Aufgabe:
 Ändern Sie MedienverwaltungClass so ab, dass sie die Daten in drei Containern speichert:
        • map mit allen Büchern
        • map mit allen CDs
        • map mit Referenzen auf alle Bücher und CDs
 Ergänzen Sie die Klasse um
        • showBucher(), die die Bücher ausgibt,
        • showCD(), die die CDs ausgibt,
        • showMedia(), die alle Medien ausgibt.

Array

         std::array lässt sich kurz und knapp charakterisieren: std::array vereint die Speicher-
         und Laufzeitanforderungen des C-Arrays mit einem STL-konformen Interface. Das
         Beste aus beiden Welten.45

Für array existieren diverse Hilfsfunktionen in der Standardbibliothek:
.[DieStandardbibliothek/arrayInterface.cpp]46
      • Z. 17, 42: Ausgabe über std::copy
      • Z. 33: Ausgabe über Range-For
      • Z. 31: {{...}}: geht auch mit einfachen Klammern, war bei ersten Compilern so nötig, um
        den Aufruf des Konstruktors zu umgehen.
      • Z. 47: std::accumulate, was in Z. 48 zur Bildung des Mittelwertes genutzt wird.
      • Z. 58: std::swap vertauscht Arrays.
      • Z. 70: Zugriff wie auf tuple möglich
Die Headerdateien der angegeben Funktionen finden sich in  und .

 Aufgabe:
 Compilieren und Analysieren Sie folgende Beispiele:
        • arrayInterface.cpp
        • DieStandardbibliothek/Aufgaben/arrayViolation.cpp47 :
          Greifen Sie über die Indexgrenzen des Arrays hinaus. Greifen Sie einmal mit dem Inde-
          xoperator [] bzw. mit der at()-Elementfunktion zu.

45
     Grimm: S. 424.
46
     Grimm: S. 424.

                                                   30
Einfach verkettete Liste

       std::forward_list  ist ein sequenzieller Container mit einem eingeschränkten Inter-
       face. Als einfach verkettete Liste benötigt sie nicht mehr Speicher als die entspre-
       chende C-Datenstruktur. std::forward_list ist für den speziellen Einsatz konzipiert:
       Wenn die optimierte Speicheranforderung, das schnelle Einfügen oder Entfernen von
       Elementen gefragt ist und der wahlfreie Zugriff nicht benötigt wird, ...48

Für forward_list existieren diverse Memberfunktionen. Im Beispiel forwardListManipulate.cpp49
wird die Verwendung vorgestellt:
     • Z. 12: .push_front() stellt ein neues Element an den Anfang der Liste.
     • Z. 27: .erase_after() löscht das Element nach der Iteratorposition.
     • Z. 31: .insert_after() fügt ein Element nach der Iteratorposition ein.
     • Z. 40: .splice_after() fügt eine Liste in eine andere ein.
     • Z. 46: .sort() sortiert die Liste.
     • Z. 52: .reverse kehrt die Reihenfolge um.
     • Z. 58: .unique() entfernt direkt aufeinanderfolgende Duplikate.

 Aufgabe50 :
       • Bestimmen Sie die Anzahl der Elemente einer std::forward_list.
       • Betrachten Sie die Musterlösung forwardListSize.cpp: Die dort implementierte Tem-
         platefunktion sizeOf funktioniert prinzipiell auch für vector.
             – Verallgemeinern Sie die Templatefunktion so, dass es auch für einen vector funk-
               tioniert.
             – Ergänzen Sie eine Spezialisierung des Templates für vector, die die Größe des
               Vectors aus der Differenz der Iteratoren (Anfang/Ende) berechnet.

Hashtabellen

Mit C++98 standen mit set, multiset, map und multimap Container zur Verfügung, die sich wie
Hashtabellen anfühlen, aber sortierte Schlüssel haben. Die Zugriffszeiten sind daher schlechter als
bei Hashtabellen. Mit C++11 gibt es nun die vier Container mit dem unordered_-Prefix. Diese
Implementierungen sind Hashtabellen.

47
   Grimm:   S. 428.
48
   Grimm:   S. 428
49
   Grimm:   Beispiel 20-22, S. 429.
50
   Grimm:   Aufg. 20-11 S. 431.

                                                31
Aufgabe51 :
 Vergleichen Sie die Zugriffszeit von map und unordered_map.
 Legen    Sie   zweigroße assoziative Container vom Datentyp              map    und
 unordered_map an und belegen sie diese mit Werten.

 Der zufällige Zugriff auf die Elemente erfolt nach Einführung der Zufallszahlen, die Zeitmes-
 sung wird bis zur Einführung der Zeitbibliothek zurückgestellt.

4.2 Algorithmen

Neue Algorithmen52 → all_of, any_of, copy_if, is_sorted, minmax_element
Mit C++17 soll begonnen werden die Parallelisieung der Algorithmen zu standardisieren.

 Aufgabe:
 Stellen Sie die Implenetierung von lambda.cpp auf die Verwendung von copy_if um.

 Aufgabe:
 Ergänzen Sie MedienverwaltungClass um eine Methode checkDuplicateAlgo, die unter Ver-
 wendung von std::any_of die Prüfung durchführt.
  template < class InputIt , class UnaryPred >
    bool any_of ( InputIt first , InputIt last , UnaryPred pred );

4.3 Neue Funktionalität

Smart Pointer

   • auto_ptr: C++03 Implementierung: Keine Unterscheidung von move und copy → passt
     nicht ins C++11-Konzept
   • unique_ptr löst auto_ptr ab.
   • shared_ptr: arbeitet mit Referenzzähler
   • weak_ptr: Wrapper um shared_ptr: „Ein nicht aktivierter shared_ptr”. Die „Aktivierung”
     erfolgt durch .lock(). Kann für den Umgang mit zyklischen Referenzen nützlich sein (s.
     Abbildung 8)

                                               32
Abbildung 8: Zyklische Referenz53

 Aufgabe:
      • Compilieren und Analysieren Sie das Beispiel (TourDeC++11/uniquePtrMove.cpp).
        Greifen Sie auf die Pointer unique1 und unique2 vor und nach dem Move zu.
      • Compilieren und analysieren Sie Beispiel anhang_a/smart_ptr01.cpp (Beispiel mit
        Deleter)

      • Compilieren und Analysieren Sie das Beispiel (TourDeC++11/sharedWeakPtr.cpp).
      • Compilieren und Analysieren Sie das Beispiel Zyklische Referenz 54 .

Reguläre Ausdrücke

Prinzipielles Vorgehen
     • Erkläre den regulären Ausdruck
       std::regex r();

     • Prüfe, ob die Zeichenkette exakt dem Muster entspricht:
       std::regex_match (,r);

51
   Grimm: Aufg. 20-13 S. 447: mapHashComparison.cpp.
52
   http://www.cplusplus.com/reference/algorithm/
53
   Linux Magazin 04/2013, S. 106.
54
   Listing 4 aus LM 03/2013

                                                33
• Halte das Ergebnis der Suche
       std::smatch s; std::regex_search(, s, r);
       s enthält nun die Treffer.

     • Suche und ersetze Treffer:
       std::regex_replace (,r,);
       Über $1, $2, ... kann dabei auf Gruppen   des regulären Ausdrucks zugegriffen werden.

 Aufgabe:
      • Kompilieren und analysieren Sie Beispiel anhang_a/regexpr.cpp55
      • Bauen Sie einen Parser für kommaseparierte Zeichenketten, die den Typ (Integer, Byte,
        Wert56 , String) erkennt.57
 Anmerkung: Es gibt einen REGEX-Tester58

Zufallszahlen

Die Erzeugung von Zufallszahlen erfolgt nach folgendem Muster:
     • Erzeugung eines Zufallszahlenstroms
     • Abbildung auf die gewünschte Verteilungsfunktion
     • Abrufen der Zufallszahlen
Im Beispiel [DieStandardbibliothek/distribution.cpp] wird die Verwendung vorgestellt:
     • Z. 25/28: Definition des Zufallszahlenstroms
     • Z. 33-39: Abbildung auf verschiedene Verteilungsfunktionen
     • Z. 47-50: Abrufen der Zufallszahlen

 Aufgabe59 :
 Ergänzen Sie die map/unordered_map-Übung:
      • Greifen Sie in einer Schleife auf mapSize/10 zufällige Schlüssel zu und geben Sie diese
        aus.

55
   Wolf: S. 436f.
56
   C++-Literal der Form 22_km
57
   Musterlösung: Regex.cpp
58
   http://regex101.com
59
   Teil von DieStandardbibliothek/Aufgaben/mapHashComparison.cpp

                                               34
Zeitbibliothek

Die C++11-Zeitbibliothek kennt folgende wichtigen Klassen:
      • Zeitpunkt std::chrono::time_point
      • Dauer std::chrono::duration
      • Zeitgeber std::chrono::system_clock
Mit folgendem Code lässt sich daher eine Zeitmessung bauen:
auto start = std :: chrono :: system_clock :: now ();
// code for measurement
std :: chrono :: duration < double > dur = std :: chrono :: system_clock :: now () - start ;
std :: cout
Typvarianten61

Alternative Typen konnten bisher über union abgebildet werden. Dabei blieb es aber dem Ent-
wickler überlassen, wie er sicher stellen will, welcher Typ gerade aktuell ist. std::variant gibt
hier Unterstützung:
using ItsOk = std :: variant < std :: vector < int > , double >;

int main () {
  // set the variant to vector , this constructs the internal vector
  ItsOk io = std :: vector {22 , 44 , 66};
  // reset to double - the internal vector is properly destroyed
  io = 13.7;
  // There ’ s no vector in the variant - throws an exception
  int i = std :: get < std :: vector < int > >( io )[2];
}

Die Verwendung von gemeinsamen Eigenschaften der Typen erfolgt über std::visit(, ):
std :: ostream & operator < < ( std :: ostream & os , MyVariant const & v ) {
  // all variants have to implement the stream operator
  std :: visit ([& os ]( auto const & e ){ os
Beispiel62

Typinformationen

Da mit den Templates, auto und decltype die starre Typisierung immer mehr aufgebrochen wird,
existieren mit C++11 erweiterte Möglichkeiten Eigenschaften von Typen zur Compilezeit zu
überprüfen.
Alle C++-Typen gehören exakt einer der folgenden Typenkategorien an:63
is_void
is_integral
is_fl oating_p oint
is_array
is_pointer
is_reference
is_member_object_pointer
is_member_function_pointer
is_enum
is_union
is_class
is_function

Davon abgeleitet gibt es zusammengesetzte Kategorien. So bedeutet is_arithmethic:
is_integral ODER is_floating_point.64 [DieStandardbibliothek/typeCategories.cpp]

Desweiteren könne Typeigenschaften abgefragt werden: is_abstract, is_polymorphic, is_signed, ...
Überblick65

  Aufgabe:
  Ändern Sie die Implementierung des Templates bigNum66 so ab, dass
     • im Falle von numerischen Argumenten der Kleinervergleich angewendet wird.
     • in allen anderen Fällen das Ergebnis von .size() verglichen wird.
  Für die Lösung gibt es zwei Ansätze:
     • is_arithmethic::type liefert true_type oder false_type. Damit lässt sich ein weites
       Funktionstemplate aufrufen, das als 3. Parameter true_type oder false_type nimmt.
     • is_arithmethic::value liefert true oder false. Damit lässt sich mit if constexpr
       innerhalb des Templates verzweigen.

62
   https://en.cppreference.com/w/cpp/utility/any
63
   Grimm: S. 343.
64
   Grimm: S. 345.
65
   http://www.cplusplus.com/reference/type_traits/|
66
   Kapitel 3.1

                                                      37
5 Multithreading
Der Lebenszyklus eines Threads lässt sich wie folgt kontrollieren:

      • Erzeugen eines Threads:
         std::thread t (, , ...);

         Die Parameter werden grundsätzlich in den Thread kopiert. Sollen sie als Referenz überge-
         ben werden, muss dies durch std::ref gekennzeichnet werden.
      • Synchronisieren von Threads
         t.join();

      • Entkoppeln von Threads
         t.detach();: Dies funktioniert zwischen Kind und Enkel-Thread. Der Hauptthread muss
         immer auf alle abgeleiteten Threads warten.

Die Sicherung von Ressourcen erfolgt über Mutex (mutual exclusion, wechselseitiger Ausschluss):
      • std::mutex m; definiert Mutex
      • std::lock_guard g(m); setzt Mutex für die Lebenszeit von g oder bis g.unlock()
Ist hingegen eine Operation atomar, so ist eine Sicherung nicht nötig. Eine atomare Operation ist
eine Operation auf einen atomaren Datentypen. Als atomare Datentypen gibt es einerseits die built
in Typen atomic_bool, atomic_char, ..., andererseits können auch benutzerdefinierte Typen als
atomar gekennzeichnet werden, indem sie in das Template atomic genommen werden. Die
Verwendung atomarer Typen zeigt [Multithreading/atomic.cpp].67
Threads lassen sich über Semaphore synchronisieren. Der „Sender” signalisiert dem „Arbeiter”,
wann er weitermachen darf. Bestandteile:
      • Einfache (boolsche) Variable: Das Semaphor
      • std::lock_guard zum Setzen des Semaphors
      • std::unique_guard zum Lesen des Semaphors
      • std::condition_variable zum Triggern des Arbeiters durch den Sender
Threads lassen sich auch über future/promise, d.h. über einen Rückgabewert synchronisieren:
    // promise definieren
            promize prom;
       // future ableiten
 fut = prom.get_future();
        // thread starten
   thread t(f, prom, ...)       f(prom, ...)
                                // thread-Bearbeitung
                                // Ergebnis-R"uckgabe
        // Ergebnis erwarten    prom.set_value(e)
               e = fut.get()

67
     Grimm: S. 230.

                                                 38
Beispiel: Multithreading/futurePromise.cpp
Sollen mehrere Threads auf dasselbe Ergebnis warten, kann dies mit einem shared future erfolgen.
Beispiel: sharedFuturePromise.cpp
Der Future/Promise-Mechanismus lässt sich über die async-Funktion vereinfachen:
     • auto f = std::async (launch::async,, ); liefert ein Future-Objekt
     • Synchronisation über f.get()
Je nach Programmierstil enthalten Programme globale Variablen. Diese verhindern, dass ein Pro-
gramm in mehreren Threads laufen kann. Eine Möglichkeit ein solches Programm threading -fähig
zu machen, ist, Variablen, die nun pro Thread benötigt werden, als „threadlokal” zu definie-
ren. Threadlokale Daten bilden ein Array, in welchem jeder Thread sein eigenes Element hat:
[Multithreading/threadLocal.cpp]68

 Aufgabe:
 Compilieren und analysieren Sie folgende Beispiele:
      • anhang_a/thread01.cpp (Threads erzeugen)
      • anhang_a/thread02.cpp (Mutex)
      • Bsp. 4-8 (conditionVariable.cpp)69
        Erweitern Sie dieses Beispiel auf mehrere worker.
      • Bsp. 18.5 (futurePromise.cpp)70
        Stellen Sie dieses Beispiel auf async um.
      • Beispiel aus Linux Magazin 04/2012 (async): dotProduct.cpp/dotProductPara.cpp
 Beim gcc ist dazu die Linker-Option -pthread nötig!

6 Quellen
 Grimm, Rainer      C++11 für Programmierer, 2014
 Wolf, Jürgen       Grundkurs C++, 3. Auflage

68
   Grimm: S. 269
69
   Grimm: S. 58.
70
   Grimm: S. 292.

                                               39
Vielen Dank für Ihre Aufmerksamkeit

     Ihr Referent und das Team von
     IT-Schulungen.com

     Fon    +49 (0) 911 650 08 - 30
     Fax    +49 (0) 911 650 08 - 399
     Mail   info@it-schulungen.com
     Web    www.it-schulungen.com

     Education Center der New Elements GmbH
     Thurn-und-Taxis-Straße 10
     90411 Nürnberg

     www.newelements.de

www.IT-Schulungen.com                              New Elements GmbH | IT-
                                                   Schulungen.com

                                              40
Sie können auch lesen