C++-Standardbibliothek
Die C++-Standardbibliothek ist die vom C++-Standardisierungskomitee der ISO festgelegte grundlegende Programmbibliothek der Programmiersprache C++. Sie enthält eine Sammlung der wichtigsten Unterprogramme und anderer grundlegender Programmkomponenten, zum Beispiel verschiedene generische Container, Funktionen zu deren Manipulierung, Funktionsobjekte, generische Zeichenketten (auch „Strings“ genannt), Datenströme u. a. für den Dateizugriff, Unterstützung von Sprachmitteln sowie einfache Funktionen (zum Beispiel aus der Mathematik). In ihr ist auch die gesamte Standardbibliothek der Programmiersprache C enthalten.
Entstehung
Die C++ Bibliothek hat ihren Ursprung in den 1980er Jahren und wurde im Laufe der Standardisierung durch den Einfluss einer bei Hewlett-Packard entwickelten Bibliothek namens Standard Template Library (STL) überarbeitet. Heute wird die C++-Standardbibliothek fälschlicherweise immer noch häufig STL genannt, obwohl es sich um zwei unabhängige Bibliotheken handelt.
Erweiterungen
Seit April 2006 gibt es eine Bibliothekserweiterung[1] (Technical Report 1, kurz TR1), die z. B. reguläre Ausdrücke, verschiedene Smartpointer, Hash Container und eine Zufallszahlbibliothek spezifiziert. Diesen Erweiterungen wurde der Namensraum std::tr1
zugeteilt. Viele dieser Erweiterungen stellen Vorschläge dar, Funktionen der Standardbibliotheken zu ändern. Einige sind seit der Veröffentlichung des C++11-Standards als reguläre Funktionen in die Standardbibliothek übernommen worden. Damit sind sie mittlerweile direkt über Namensraum std
erreichbar.
Seit 2012 werden neue Funktionen der Standardbibliothek und von C++ allgemein nicht mehr als Technical Reports, sondern als Technical Specification formatiert und sind deshalb im Namensraum std::experimental
enthalten.
Bestandteile der C++-Standardbibliothek
Die C++-Standardbibliothek bietet:
- Container (Behälterklassen)
- Iteratoren
- Algorithmen
- Funktionsobjekte
- Zeichenketten
- Eingabe und Ausgabe
- Lokalisierung
- Numerik
- Ausnahmen
- RTTI (Runtime Type Information)
- Zufallszahlengeneratoren und Transformatoren für Wahrscheinlichkeitsverteilungen (seit C++11)
- Werkzeuge für Multithreading (seit C++11)
- Reguläre Ausdrücke (seit C++11)
- Werkzeuge zur Zeitmessung (seit C++11)
Die meisten Komponenten der C++-Standardbibliothek liegen in Form von Vorlagenklassen (engl.: „Templates“) vor. Dieses Konzept hat den großen Vorteil der Wiederverwendbarkeit, so können zum Beispiel durch einfache Deklaration Container für beliebige Datentypen erzeugt werden; Algorithmen gelten für eine ganze Reihe von Datentypen. Weiterhin wird durch Templates schon während des Kompilierens eine Typsicherheit sichergestellt, die Laufzeitfehler minimiert. Ein Nachteil sind die überaus schwer zu lesenden Fehlermeldungen, die zum Beispiel bei Typ-Konflikten erzeugt werden.
Container
Container (Behälterklassen) sind Objekte, die andere Datentypen und Objekte speichern, zum Beispiel Listen und Felder. Zum Zugriff auf die einzelnen Elemente werden vom Container Methoden und Iteratoren zur Verfügung gestellt. Der Container kümmert sich um die Speicherverwaltung für die Elemente[2] und hat deswegen Funktionen zum Einfügen und Löschen von Elementen. Der Container besitzt die Elemente. Das bedeutet, dass die Lebenszeit eines gespeicherten Objekts nicht die Lebenszeit der Liste übersteigt.[3] Wenn der Inhalt danach benötigt wird, muss der Benutzer entweder Kopien davon erstellen oder selbst allokierte Pointer verwenden.
In sequenziellen Containern sind die Objekte linear angeordnet. In assoziativen Containern erfolgt der Zugriff mit Hilfe von Schlüsseln.
Name | Klassenname | Beschreibung | Verfügbar seit |
Felder dynamischer Größe, Vector | std::vector | Einfügen und Löschen am Ende ist in und für anderen Elemente in möglich.
Der Container unterstützt wahlfreien Zugriff (Random Access) in . |
|
Felder fester Größe | std::array | Die Größe muss bereits während des Kompiliervorgangs fest stehen. | C++11 |
doppelt verkettete Listen | std::list | Einfügen und Löschen ist in möglich. Wahlfreier Zugriff ist nicht möglich. | |
einfach verkettete Listen | std::forward_list |
Einfügen und Löschen ist in möglich. Wahlfreier Zugriff und Grössenabfrage sind nicht möglich | C++11 |
Warteschlangen | std::queue | Der Container unterstützt keine Iteratoren. | |
Warteschlangen mit zwei Enden | std::deque | Der Datentyp verhält sich wie der Vector, kann jedoch Elemente am Anfang und Ende in einfügen. | |
Warteschlangen mit Prioritäten | std::priority_queue | Die Struktur garantiert wie der Heap, dass immer das Element mit der höchsten Priorität am Beginn steht. | |
Stapel | std::stack | Der Container unterstützt keine Iteratoren. |
Name | Klassenname | Beschreibung |
Mengen | std::set und std::multi_set | Sets sind assoziative Container in denen einzigartige Elemente als Schlüssel gespeichert sind. |
Assoziative Felder (Maps) | std::map und std::multi_map | Maps speichern Elemente zusammen mit ihren Schlüsseln in einer strict weak ordering. Jeder Schlüssel muss einzigartig sein. |
Name | Klassenname | Beschreibung | Verfügbar seit |
Mengen | std::unordered_set und std::unordered_multiset | Die Sequenz wird durch eine Hashfunktion sortiert. Erst seit technical review 1 verfügbar. | C++11 |
Assoziative Felder | std::unordered_map und std::unordered_multimap | Im Gegensatz zur Map werden die Daten unsortiert gespeichert. Intern werden Hashtabellen als Index verwendet. Erst seit technical review 1 verfügbar. | C++11 |
Name | Klassenname | Beschreibung |
Bitmengen | std::bitset | Das Bitset verhält sich sehr ähnlich wie ein normales Array, aber ist auf Platzverbrauch optimiert, da der kleinste Datentyp in C++ char mit mind. 7 Bit ist.[4] |
Iteratoren
Iteratoren (von lateinisch iterare: wiederholen) sind intelligente Zeiger, mit deren Hilfe über die Elemente eines Containers iteriert sowie auf einzelne Elemente des Containers zugegriffen werden kann. Die Iteratoren bilden ein zentrales Konzept für die Container. Bezogen auf ihre Aufgabe sind die Iteratoren reine Zugriffsobjekte. Sie entkoppeln Algorithmen von den Containern, so dass Algorithmen unabhängig von Containertypen formuliert werden können. Das nachfolgende Diagramm zeigt das Verhältnis des Iterators zu den Containern und Algorithmen:
Bei den Iteratoren gibt es folgende Kategorien:
- Eingabe-Iteratoren: lesenden Zugriff für einen einzelnen Durchlauf
- Ausgabe-Iteratoren: schreibender Zugriff für einen einzelnen Durchlauf
- Forward-Iteratoren: sequenzieller Zugriff mit relativem Bezug auf Iteratoren, in eine Richtung
- Bidirektionale Iteratoren: wie Forward-Iteratoren jedoch in beide Richtungen
- Iteratoren mit wahlfreiem Zugriff: wahlfreier Zugriff, auch mit Index-Operator ([])
Dabei stellt nicht jeder Container alle Iteratoren zur Verfügung. Der list-Container lässt z. B. keinen wahlfreien, sondern nur sequentiellen Zugriff zu. Die Ein- und Ausgabeiteratoren sind dagegen sehr allgemein und werden grundsätzlich bereitgestellt.
Algorithmen
Algorithmen sind Funktionen mit bestimmten Manipulationsvorschriften, die auf einen Container angewendet werden. Dabei sind sie unabhängig von der speziellen Implementierung der Container. Sie können nur über Iteratoren auf die Elemente in den Containern zugreifen. Sie enthalten u. a. die Standard-Algorithmen der Informatik, wie z. B. Sortieralgorithmen oder Verfahren zur Erzeugung von Zufallszahlen. Die meistbenutzten sind:
std::for_each
: wendet eine Operation auf alle Elemente eines Datensatzes anstd::transform
: transformiert einen Datensatz mit einer Funktion in einen anderenstd::copy
: kopiert den Datensatz in einen anderenstd::sort
: sortiert den Datensatzstd::find
: sucht nach einem bestimmten Element in einem Datensatzstd::search
: sucht nach einer Elementreihe in einem Datensatz
Diese und weitere Algorithmen befinden sich im Header <algorithm>
.
Funktionsobjekte
Bei Funktionsobjekten oder Funktoren handelt es sich um Objekte, die als Funktion aufgerufen werden können. Hierbei wird der Funktionsoperator „operator()
“ überladen. Bei den Funktionsobjekten gibt es folgende Kategorien:
- Generatoren ohne Funktionsparameter „f()“
- Unäre Funktionen mit einem Funktionsparameter „f(x)“
- Binäre Funktionen mit zwei Funktionsparametern „f(x,y)“
Grundsätzlich benötigen die Algorithmen der C++-Standardbibliothek keine Funktionsobjekte mit mehr als zwei Parametern.
Zeichenketten
Die Zeichenketten-Bibliothek definiert ein Klassen-Template std::basic_string
zur Darstellung von Zeichenketten (engl.: "strings") variabler Länge. Die Methoden des Klassen-Templates bieten Manipulationen und -Operationen, wie z. B. das Einfügen, Löschen, Ersetzen und Suchen in Zeichenketten. Von diesem Klassen-Template gibt es zwei Typdefinitionen:
std::string
ist eine Instanzierung vonstd::basic_string
, parametrisiert mitchar
.char
kann ein Zeichen des Basiszeichensatzes speichern.std::wstring
ist eine Instanzierung vonstd::basic_string
, parametrisiert mitwchar_t
(wide character).wchar_t
kann alle Elemente des größten unterstützten Zeichensatzes speichern.
Beispiele
std::vector<int> daten(10); // Datenfeld mit int der Länge 10 anlegen, [0] .. [9]
// Iterator anlegen und initialisieren; Iterator zeigt dann auf ersten Eintrag (also Index 0).
std::vector<int>::iterator dIter(daten.begin());
// Zähler i initialisieren,
for (int i = 0;
// Schleife solange durchgehen, bis dIter auf erste Position NACH dem Ende des Datenfeldes zeigt (also Index 10).
dIter != daten.end();
// Zähler i erhöhen, Iterator auf den nächsten Eintrag zeigen lassen.
++i, ++dIter) {
// i dem Datenfeld zuweisen, auf das dIter zeigt.
*dIter = i;
}
// daten: 0 1 2 3 4 5 6 7 8 9
std::vector<int> datenZwei(10);
std::copy(daten.begin(), daten.end(), // welche Daten sollen kopiert werden
datenZwei.begin()); // und wohin
// datenZwei: 0 1 2 3 4 5 6 7 8 9
// binärer Funktor std::multiplies<int>() braucht zwei Argumente
std::transform(
daten.begin(), daten.end(), // auf welchem Bereich soll Algorithmus arbeiten
datenZwei.begin(), // zweite Datenquelle
datenZwei.begin(), // wohin sollen Ergebnisse gehen
std::multiplies<int>()); // miteinander multiplizieren
// datenZwei: 0 1 4 9 16 25 36 49 64 81
// unärer Funktor std::negate<int>() braucht ein Argument
std::transform(daten.begin() + 4, // dieses Mal nicht vom Anfang, sondern vom fünften Element an
daten.end(),
daten.begin() + 4, // dito, Ziel
std::negate<int>()); // Negation
// daten: 0 1 2 3 -4 -5 -6 -7 -8 -9
std::sort(daten.begin(), daten.end()); // sortieren
// daten: -9 -8 -7 -6 -5 -4 0 1 2 3
Siehe auch
Weblinks
- C++-Referenz (englisch)
- Apache C++ Standard Library (STDCXX) (englisch)
- Standard Template Library Programmer’s Guide SGI
Einzelnachweise
- TR1 (PDF; 1,43 MB)
- cplusplus.com
- Container. sgi.com
- cplusplus.com bitset