7

Click here to load reader

C++ 11-Standard - public.beuth-hochschule.depublic.beuth-hochschule.de/~kempfer/skript_cpp/cpp_11_standard.pdf · C++ 11-Standard Der letzte Standard für C++ wurde in 2003 verabschiedet

Embed Size (px)

Citation preview

Page 1: C++ 11-Standard - public.beuth-hochschule.depublic.beuth-hochschule.de/~kempfer/skript_cpp/cpp_11_standard.pdf · C++ 11-Standard Der letzte Standard für C++ wurde in 2003 verabschiedet

C++ 11-Standard Der letzte Standard für C++ wurde in 2003 verabschiedet. Durch die Weiterentwicklung der Programmierung sowie der Einführung neuer Features in anderen Programmiersprachen, die natürlich die C++-Programmierer auch verwenden möchten, war es notwendig, einen neuen Standard zu definieren. Dabei sind aber einige wesentliche Änderungen notwendig, und die Definition des Standards benötigte lange Zeit, da immer auch auf die Kompatibilität zu dem vorigen Standard geachtet werden muss. Zuerst ging man davon aus, dass der neue Standard in 2008 oder 2009 verabschiedet werden würde; daher wurde er 0x (manchmal auch 200x) genannt. Auch als im Jahr 2010 der neue Standard noch nicht verabschiedet wurde, wurde die Bezeichnung nicht geändert (schließlich könnte ja bis 2019 ein weiterer Standard C++ 1x zustande kommen). Durch die Verabschiedung des Standards am 11.10.2011 wurde es offiziell zum Standard ISO/IEC 14882:2011, kurz C++ 11. Der neue Standard hat drei Hauptziele:

1. C++ soll das Mittel der Wahl für systemnahe Programmierung bleiben. 2. Es soll das Erstellen leistungsfähiger Bibliotheken ermöglichen. 3. Es soll leichter zu vermitteln und zu lernen sein.

Doppelte >> vector<vector<int>> Matrix;

Bisher wird durch das Erkennen des längst möglichen Tokens das >> als Eingabeoperator erkannt. Um das Problem zu umgehen, musste ein Leerzeichen eingesetzt werden. Im 11-Standard wird diese Zeile korrekt erkannt, da < und > jetzt als Klammerpaar erkannt wird und dies eine höhere Priorität als der Eingabeoperator hat. Neuer Nullzeiger Es wird ein neuer Nullzeiger eingeführt mit dem Namen nullptr. Dieser ersetzt den bisherigen NULL-Zeiger. Das Problem, das damit beseitigt wird, liegt in der impliziten Typumwandlung des NULL-Zeigers. Dies lässt sich mit dem folgenden Beispiel verdeutlichen: void f(char *);

void f(int);

f(0); // ruft f(int) auf

f(NULL); // ruft auch f(int) auf

f((char *) NULL); // ruft jetzt f(char *) auf

f(nullptr); // ruft im C++ 11-Standard problemlos f(char *) auf

Verallgemeinerte Initialisierungslisten Bisher gab es mehrere Möglichkeiten, Objekte und Variablen zu initialisieren. Im Fall von Typ(value)

wird je nach Definition mal ein Konstruktor aufgerufen und mal ein Typecasting durchgeführt. Gerade in Templates ist dies nicht immer eindeutig und damit sehr gefährlich. Im 11-Standard werden daher die verallgemeinerten Initialisierungslisten (Generalized Initializer Lists) eingeführt. Sie sind eine Liste von Elementen, die in geschweifte Klammern eingeschlossen werden. Typ var1 = Typ{1, 2};

Page 2: C++ 11-Standard - public.beuth-hochschule.depublic.beuth-hochschule.de/~kempfer/skript_cpp/cpp_11_standard.pdf · C++ 11-Standard Der letzte Standard für C++ wurde in 2003 verabschiedet

Typ var2 = {1, 2};

Typ var3{1, 2};

return {1, 2, 3};

Klassen können dann auch mit dem Sequenz-Konstruktor ausgestattet werden, der eine Initialisierungsliste als Argument vom Typ std::initializer_list<T> bekommt. Dabei muss die Größe der Liste nicht bekannt sein. #include <initializer_list>

using namespace std;

class MyVec

{

int *Array;

public:

MyVec(initializer_list<int> Seq): Array{new int[Seq.size()]}

{ copy(Seq.begin(), Seq.end(), Array); }

};

MyVec Primzahlen{2, 3, 5, 7, 11, 13, 17};

Bei der Initialisierung mit {} hat ein eindeutig passender Sequenz-Konstruktor Vorrang gegenüber dem Standard-Konstruktor, bevor das bisherige Verhalten greift. Range For - Schleife Bisher konnte mit auto bei der Deklaration von Variablen angegeben werden, dass sie lokal sind (bei globalen Variablen durfte es nicht verwendet werden). Da dieses Schlüsselwort optional war und in der Praxis wohl niemand verwendet hat, wurde es umdefiniert. Bisher musste z.B. für die Ausgabe aller vector-Elemente etwas umständlich der Iterator verwendet werden: void drucken(const vector<int> &v)

{

for (vector<int>::const_iterator i = v.begin(); i != v.end(); i++)

cout << *i << endl;

}

Mit dem neuen Range For (for-Schleife mit nur einem Parameter) kann diese Schleife viel übersichtlicher geschrieben werden. Durch die Verwendung der neuen Definition von auto wird der Typ automatisch ermittelt. Die Range For-Schleife ermittelt dann automatisch den Bereich. void drucken(const vector<int> &v)

{

for (const auto &i: v)

cout << *i << endl;

}

Zusammen mit Initialisierungslisten kann auch ohne Verwendung von Klassen sehr übersichtlicher Quelltext geschrieben werden: void Primzahlen()

{

Page 3: C++ 11-Standard - public.beuth-hochschule.depublic.beuth-hochschule.de/~kempfer/skript_cpp/cpp_11_standard.pdf · C++ 11-Standard Der letzte Standard für C++ wurde in 2003 verabschiedet

for (const auto Prim: {2, 3, 5, 7, 11, 13, 17} )

cout << Prim << endl;

}

Move-Konstruktor (RValue References) Werden große Objekte als Funktionsergebnis zurückgegeben, wird auch viel Speicherplatz verschwendet. Daher wurden bereits früher die Referenzen eingeführt. Damit kann aber eine lokale Variable nicht als Funktionsergebnis zurück gegeben werden. Der Hintergrund ist folgender: Teilausdrücke, die temporäre Werte erzeugen, werden RValues genannt. Im allgemeinen kann keine Adresse zu diesem Wert ermittelt werden. Dazu gehören auch die Funktionsergebnisse, in denen eine lokale Variable zurückgegeben werden, da die lokale Variable nach Funktionsende nicht mehr existiert. Die Lücke wird mit sogenannten RValue References geschlossen: struct Liste

{

Data *data;

Liste(Liste &&tmp): data(tmp.data) // Move-Konstruktor wg. &&

{ tmp.data = nullptr; }

~Liste()

{ delete data; }

};

Liste getNamen(int PLZ)

{

Liste Nachnamen;

Liste Vornamen;

bool besserVornamen = getFromDatabase(Vornamen, Nachnamen, PLZ);

return besserVornamen ? Vornamen : Nachnamen; // Rückgabe lokaler Obj.

}

int main()

{

Liste alle(getNamen(13353));

}

Lambda-Ausdrücke In anderen Programmiersprachen gibt es sie bereits: Anonym konstruierte Objekte und Funktionen. In C++ werden diese Funktore genannt und sind entweder als Zeiger auf Funktionen oder mittels überladenem operator() definiert. struct PrintOp

{

void operator()(int i)

{ cout << i << endl; }

};

void printAll(vector<int> &v)

{

for_each(v.begin(), v.end(), PrintOp());

}

Page 4: C++ 11-Standard - public.beuth-hochschule.depublic.beuth-hochschule.de/~kempfer/skript_cpp/cpp_11_standard.pdf · C++ 11-Standard Der letzte Standard für C++ wurde in 2003 verabschiedet

PrintOp ist durch den überladenen operator() ein Funktor. Allerdings eleganter geht es mit den Lambda-Ausdrücken: void printAll(vector<int> &v)

{

for_each(v.begin(), v.end(), [](int i) { cout << i << endl; } );

}

Die Angabe von [] bedeutet, dass die anonyme Funktion keinen Zugriff auf Variablen der Umgebung hat. Um den Zugriff auf Variablen zu erlauben, müssen diese in den eckigen Klammern eingefügt werden (entspricht der Übergabe als Parameter); für den Zugriff auf alle Variablen als Referenzparameter wird [&] und als Werteparameter [=] verwendet. Vorsicht ist bei der Verwendung der Werteparameter geboten, da die Variablen komplett kopiert werden müssen, und dies kann wieder zu Performance-Einbußen führen. void printAll(vector<int> &v)

{

int cnt = 0, cnt2 = v.size();

for_each(v.begin(), v.end(),

[&cnt](int i) { cout << cnt++ << “: “ << i << endl; } );

// nur Zugriff auf cnt, nicht aber auf cnt2

}

Variadic Templates Es können nun Templates mit variabler Argumentenlänge erstellt werden. Damit könnte jetzt z.B. eine vereinfachte printf-Funktion ohne Formatierungsanweisungen auch als Template-Funktion geschrieben werden: template<typename T, typename ...Us>

void xprintf(const char *s, T t, Us... args)

{

while (s && *s) // s ist kein nullptr & zeigt auch nicht auf string-Ende

{

if (*s == ‘%’)

{

cout << t;

return xprintf(++s, args);

}

cout << *s++;

}

}

Damit kann in der Standardbibliothek neben der Template-Klasse pair jetzt auch die Template-Klasse tuple angeboten werden. #include <tuple>

using namespace std;

int main()

{

tuple<int, double, char> tp{5, 1.5, ‘A’};

Page 5: C++ 11-Standard - public.beuth-hochschule.depublic.beuth-hochschule.de/~kempfer/skript_cpp/cpp_11_standard.pdf · C++ 11-Standard Der letzte Standard für C++ wurde in 2003 verabschiedet

cout << std::get<1>(tp) << endl; //

}

Aufrufen und Erben von Konstruktoren Es ist jetzt möglich, dass ein Konstruktor einen anderen Konstruktor der Klasse aufruft, so dass der Initialisierungscode nicht mehrmals geschrieben oder in eine andere Methode ausgelagert werden muss. Der Aufruf geschieht genauso wie beim Aufruf des Konstruktors der Oberklasse. struct Koord

{

int x, y, z;

Koord(int a, int b, int c): x(a), y(b), z(c) { }

Koord(int all): Koord(all, all, all) { }

};

Beim Ableiten dieser Klasse werden bisher die Methoden, nicht aber die Konstruktoren vererbt. Es müssten also alle Kombinationen von Konstruktoren auch in der abgeleiteten Klasse definiert werden. Mit using können jetzt auch die Konstruktoren geerbt werden. struct Spez_Koord: public Koord

{

using Koord::Koord; // erbt jetzt auch die Konstruktoren

// ...

}

Noch konstanter An manchen Programmstellen verlangt der Compiler einen konstanten Ausdruck, z.B. beim Anlegen eines Arrays oder die Vergleichswerte in einer switch-Anweisung. Hier konnten wohl Literale und const-Ausdrücke aber keine const-Funktionen verwendet werden. Aber einige dieser Funktionen könnten bereits zur Compilierzeit errechnet werden und liefern dann wieder einen konstanten Ausdruck. Dafür wurde das Schlüsselwort constexpr eingeführt. Dies weist den Compiler an, den Ausdruck zur Compilierzeit zu errechnen. Gelingt dies nicht, wird ein Compilerfehler ausgegeben. constexpr int max(int a, int b)

{ return (a > b) ? a : b; }

int Array[max(3, 4)];

int i = 4;

switch (i)

{

case max(3, 4): // ...

}

Kopierkonstruktor und Zuweisungsoperator Wie bisher werden Kopierkonstruktor und Zuweisungsoperator automatisch zu einer Klasse hinzugefügt, wenn sie nicht vom Programmierer explizit geschrieben werden. Da hier die Eigenschaften nur 1:1 kopiert werden (die reservierten Speicherbereich dagegen nicht!),

Page 6: C++ 11-Standard - public.beuth-hochschule.depublic.beuth-hochschule.de/~kempfer/skript_cpp/cpp_11_standard.pdf · C++ 11-Standard Der letzte Standard für C++ wurde in 2003 verabschiedet

kann eine Zuweisung gefährlich werden. Dieser Automatismus lässt sich unterbinden, indem beide Methoden als private deklariert und nicht definiert werden. Jetzt geht es einfacher und übersichtlicher mit der delete-Markierung. struct K

{

K(const K &) = delete;

K &operator=(const K &) = delete;

};

Um deutlich zu machen, dass das Standardverhalten explizit gewünscht ist, wird dagegen die default-Markierung verwendet. struct K

{

K(const K &) = default;

K &operator=(const K &) = default;

};

Datentyp ermitteln Mit dem neuen Operator decltype lässt sich der Datentyp eines Ausdruckes ermitteln. Threads Nun wird auch C++ für Multi-Thread-Programme eingesetzt werden können. Dabei werden Thread-Objekte, Mutexen (Sperren für gemeinsam genutzte Resourcen) und Wartebedingungen hinzugefügt. #include <thread>

struct T

{

string msg;

T(const string &m): msg(m) { }

void operator()()

{ cout << “Hier ist Thread “ << msg << endl; }

};

int main()

{

T t(“Nr. 1”);

std::thread th1(T);

std::thread th2([]() { cout << “Hier ist Thread Nr. 2” << endl; });

th1.join(); // wartet, bis Thread 1 fertig ist

th2.join(); // wartet, bis Thread 2 fertig ist

}

Die neue Funktion async() übernimmt von sich aus das Erzeugen und Starten von neuen Threads. Die Entscheidung wird in Abhängigkeit der Plattform, d.h. des freien Speichers, der Prozessorkerne, usw., gefällt. Bei einem schwachen System wird im schlimmsten Fall alle Threads wieder sequentiell abgearbeitet.

Page 7: C++ 11-Standard - public.beuth-hochschule.depublic.beuth-hochschule.de/~kempfer/skript_cpp/cpp_11_standard.pdf · C++ 11-Standard Der letzte Standard für C++ wurde in 2003 verabschiedet

Neue Klassen in der STL Hinzugekommen sind weitere Template-Klassen wie z.B. unorderd_map<>, unorderd_set<>, array<> (nicht-dynamisches Array) und forward_list<> (einfach verkettete Liste). Auch die bestehenden Template-Klassen wurden teilweise überarbeitet; so sind eine ganze Reihe von Funktionen und Methoden hinzugefügt. Timer Bisher war man bei Timern in C++ immer auf Bibliotheks- oder Betriebssystemfunktionen angewiesen. In der neuen Headerdatei <chrono> werden Zeitspannen und –punkte sowie Stoppuhr und einfache Operationen darauf implementiert. Zufallszahlen In der neuen Headerdatei <random> sind verschiedene Zufallsgeneratoren definiert, mit denen bessere Zufallszahlen als bisher erzeugt werden können. Zusätzlich können auch externe Zufallsgeneratoren mit eingebunden werden. Garbage Collection Nein, es ist noch keine Garbage Collection in C++ implementiert. Aber es wurde eine Schnittstelle geschaffen, um externe Garbage Collection einzubinden. Vielleicht wird sie ja im Standard 1x mal implementiert sein. Concepts Bjarne Stroustrup hätte so gerne die über Jahre entwickelten Concepts mit im Standard gesehen. Aber das Komitee für den Standard hat diese wohl noch zurückgestellt. Auch diese werden dann vielleicht im Standard 1x enthalten sein.