Sequenzielle Container
Beginnen wir den Einstieg in die Container der Standardbibliothek mit den sequenziellen Containern. Bei allen sequenziellen Containern erfolgt der Zugriff auf gespeicherten Daten über deren Position im Container.
Wenn nicht explizit ausgeschlossen, gelten weiterhin die Aussagen des Kapitels Gemeinsame Container-Eigenschaften und -Operationen für alle nachfolgend beschriebenen Container.
Containertypen
array
Einzubindende Header-Datei: array
Ein Array ist ein Container mit einer fixen Anzahl von Elementen und entspricht somit zunächst einem Feld. Der Vorteil eines Arrays gegenüber einem Feld ist, dass zum einen beim Zugriff auf ein Element optional eine Bereichsüberprüfung stattfindet und zum anderen ein Array in den später beschriebenen Algorithmen der Standardbibliothek verwendet werden kann. Die Elemente eines Arrays liegen kontinuierlich im Speicher.
vector
Einzubindende Header-Datei: vector
Ein Vektor ist ein Container, dessen Größe sich zur Laufzeit an die Anzahl der in ihm abgelegten Elemente anpasst. D.h., die bei der Definition eines Vektors angegebene Größe kann sich später durch Löschen und Hinzufügen von Elementen ändern. Die Elemente eines Vektors liegen ebenfalls kontinuierlich im Speicher.
Steht die maximale Anzahl der Elemente von vornherein fest, sollte der Effizienz wegen anstelle eines Vektors ein Array verwendet werden.
Die Standardbibliothek enthält einen speziellen Vektor std::vector<bool> zum Abspeichern von bool Daten. Dieser Vektor besitzt einige Einschränkungen und ist von der Bearbeitungsgeschwindigkeit etwas langsamer als der 'normale' Vektor, benötigt dafür aber weniger Speicherplatz.
deque
Einzubindende Header-Datei: deque
Ein Deque gleicht im Prinzip einem Vektor, jedoch mit dem Unterschied, dass ein Deque das schnelle(!) Einfügen von Elementen am Anfang und Ende ermöglicht, während dies bei einem Vektor nur am Ende möglich ist. Bei einem Deque liegen die Elemente nicht mehr kontinuierlich im Speicher, sondern werden in Speicherblöcken verwaltet.
list
Einzubindende Header-Datei: list
Eine Liste wird in der Regel durch eine doppelt-verkettete Liste realisiert. Der Vorteil einer solchen doppelt-verketteten Liste ist, dass sich schnell Elemente an beliebiger Position einfügen und löschen lassen, da keine Elemente verschoben werden müssen. Beim Einfügen bzw. Löschen werden nur die Verweise auf das Vorgänger- und Nachfolgerelement umgesetzt.
forward_list
Einzubindende Header-Datei: forward_list
Die forward_list ist eine Variante von list, die nur eine einfach-verkettete Liste verwendet, d.h., ein Element enthält lediglich einen Verweis auf sein Nachfolgeelement. Durch diese Einschränkung kann eine forward_list nur vom Anfang zum Ende hin durchlaufen werden.
Container-Objekte definieren
Objekte vom Typ array haben eine von den anderen Containern abweichende Definition, weshalb wir uns diesen Container als Erstes ansehen.
Ein Array-Objekt wird wie folgt definiert:
std::array<DTYP,SIZE> obj [{ILIST}];
DTYP ist der Datentyp der im Array abzulegenden Daten. SIZE definiert die Größe des Arrays und muss eine zur Compilezeit berechenbare Konstante sein. obj ist der Name des Array-Objekts.
Um ein Array bei seiner Definition zu initialisieren, sind die Initialwerte in geschweiften Klammern aufzulisten. Sind weniger Initialwerte angegeben als Elemente im Array enthalten sind, werden die restlichen Elemente mit 0, bzw. bei abzulegenden Objekten durch Aufruf des Standardkonstruktors, initialisiert.
Im Gegensatz zu den anderen Containertypen, müssen die in einem array abzulegenden Objekte explizit instanziiert werden.
// array für int-Werte
// 4. und 5. Element ist 0
std::array<int,5> intArray {1,2,3};
// char array
// abschließende 0 mit berücksichtigen
std::array<char,6> text {"Hallo"};
// Array für CData3-Objekte
// ctor von CData3 hat einen string-Parameter
std::array<CData3,2> demObj {CData3{"eins"s}, CData3{"zwei"s}};
Objekte der anderen sequenziellen Container werden wie folgt definiert, wobei cont den Containertyp, z.B. vector oder deque, angibt und DTYP den Datentyp der Container-Elemente.
cont<DTYP> obj;
Container ohne Elemente (leerer Container)
cont<DTYP> obj(size_type count);
Container mit count Elemente, die mit dem Standardkonstruktor bzw. mit 0 initialisiert werden.
cont<DTYP> obj(size_type count, const DTYP& value);
Container mit count Elemente, die mit dem Datum value initialisiert werden. Werden im Container Objekte abgelegt, enthält value in geschweiften Klammern die Parameter eines beliebigen Konstruktors des Objekts.
cont<DTYP> obj(iiter first, iiter last);
Kopiert die Elemente aus dem Bereich [first...last) in den Container. Für die Bereichsangabe können sowohl Zeiger wie auch Iteratoren verwendet werden.
cont<DTYP> obj(const cont<DTYP>& other);
Kopiert die Elemente aus dem Container other.
cont<DTYP> obj {ilist};
Kopiert die Elemente aus der Initialisiererliste ilist in den Container. Dabei werden zunächst alle Objekte der Initialisiererliste instanziiert und erst danach in den Container kopiert. D.h., es wird für jedes Objekt der Initialisiererliste zuerst dessen Konstruktor aufgerufen und dann der Kopierkonstruktor, um die Objekte in den Container zu übernehmen. Es ist nicht möglich, anstelle des Kopierkonstruktors den Move-Kopierkonstruktor zur Ausführung zu bringen!
// Leeren Vektor definieren
std::vector<int> vect1;
// Vektor duplizieren
std::vector<int> vect2(vect1);
// Liste mit 5 Elementen definieren
std::list<int> list1(5);
// Deque mit 5 float-Werten definieren und mit 10 init.
std::deque<float> deq1(5,10.0f);
// int-Feld definieren/initialisieren
int ifeld[] {1,2,3,4};
// Vektor mit Feldinhalt initialisieren
std::vector<int> intVect(ifeld,
ifeld + sizeof ifeld / sizeof(int));
// Liste definieren und mit Initialisierliste belegen
std::list<int> list2 {11,22,33,44};
// Deque mit 3 CData3 Objekten definieren
// CData3-ctor Parameter ist std::string_view
std::deque<CData3> demDeq {{"eins"s},{"zwei"s},{"drei"s}};
// Vektor mit 2 NewDemo Objekten definieren
// NewDemo-ctor Parameter ist const char* und int
std::vector<NewDemo> nvect {{"eins",1},
{"zwei",2},{"drei",3}};
Für den Datentyp DTYP des Containers kann ebenso ein weiterer Containertyp angegeben werden. So definiert und initialisiert die Anweisung
std::array<std::array<int,3>,2>
multiArray {{{1,-2,0},{-3,7,-8}}};
ein zweidimensionales array. Das innere Array std::array<int,3> steht für die Spalten der Matrix und das äußere std::array<...,2> für die Zeilen. Dabei ist zu beachten, dass der Datentyp der Elemente nur beim inneren Array angegeben wird. Der Datentyp für das äußere Array ist 'inneres Array'.
Beachten Sie die Anzahl der geschweiften Klammern beim Initialisierungsausdruck. Der gesamte Ausdruck ist in doppelte Klammern einzuschließen.
Container und Iteratoren
Für den Zugriff auf Container-Elemente können folgende Iteratoren eingesetzt werden:
| Container | Max. möglicher Iterator |
|---|---|
| array | Random-Access Iterator |
| vector | Random-Access Iterator |
| deque | Random-Access Iterator |
| list | Bidirektionaler Iterator |
| forward_list | Forward Iterator |
Zusätzlich steht für alle Container, außer der forward_list, der Reverse-Iterator Adapter zur Verfügung. Dabei gilt zu beachten, dass ein Inkrementieren des Reverse-Iterators den Container vom Ende zum Anfang hin durchläuft.
Um über einen Iterator auf ein Element zuzugreifen, ist der Iterator zu dereferenzieren.
#include <print>
#include <vector>
int main()
{
// Vektor definieren und initialisieren
std::vector<int> obj1 {1,2,3,4};
// Iterator auf Beginn des Vektors
auto iter = obj1.begin();
// 1. Element ausgeben
std::println("1. Element: {}",*iter);
// Iterator auf 3. Element und ausgeben
iter += 2;
std::println("3. Element: {}",*iter);
// Vektor in umgekehrter Reihenfolge ausgeben
for (auto riter=obj1.rbegin();
riter!=obj1.rend(); ++riter)
std::print("{}, ",*riter);
}
1. Element: 1
3. Element: 3
4, 3, 2, 1,
Soll ein Container sequenziell durchlaufen werden, kann hierfür die range-for-Schleife verwendet werden.
Soll der Zugriff auf einen konstanten Container erfolgen, ist ein entsprechender const-Iterator hierfür zu verwenden.
Elemente einfügen und löschen
Auch hier weicht die Handhabung des Containers array von den übrigen sequenziellen Containern ab. Er lässt kein Einfügen oder Löschen von Elementen zu, da er intern über ein Feld mit fester Größe realisiert ist. Nur die folgenden Methoden sind definiert:
void fill (const DTYP& value);
Füllt das Array mit dem Datum value.
void swap (CTYP& other);
Vertauscht den Inhalt von zwei Arrays. Die zu vertauschenden Arrays müssen den gleichen Datentyp besitzen, d.h. sowohl der Datentyp der Elemente wie auch die Größe müssen übereinstimmen.
Einfügen am Anfang
Nur für die Container deque, list und forward_list ist das Einfügen von Elementen am Anfang definiert.
void push_front (const DTYP& value);
Fügt das Datum value am Anfang des Containers ein.
DTYP& emplace_front (ilist args);
Erstellt ein Datum vom Typ DTYP und fügt es am Anfang des Containers ein. args ist eine Auflistung der Parameter eines Konstruktors des einzufügenden Objekts. Die Methode liefert eine Referenz auf das eingefügte Objekt zurück.
Anfügen am Ende
Nur für die Container vector, deque und list ist das Anfügen von Elementen am Ende des Containers definiert.
void push_back (DTYP& value);
Fügt das Datum value am Ende des Containers ein.
DTYP& emplace_back (ilist args);
Erstellt ein neues Objekt und fügt es am Ende des Containers ein. args ist eine Auflistung der Parameter eines Konstruktors des einzufügenden Objekts. Die Methode liefert eine Referenz auf das eingefügte Objekt zurück.
#include <iostream>
#include <deque>
#include <iterator>
import CData3;
int main()
{
// Leeres Deque definieren
std::deque<CData3> theDeque;
// CData3-Objekt definieren
CData3 obj{"toPush"};
// CData3-Objekte an Anfang des Deque einfuegen
theDeque.push_front(obj);
theDeque.emplace_front("toEmplace");
// Deque ausgeben
for (auto iter=theDeque.begin(); iter!=theDeque.end();++iter)
std::cout << *iter << '\n';
// CData3-Objekt ans Ende anfuegen
std::cout << "------\n";
theDeque.emplace_back("theEnd");
for (auto iter=theDeque.begin(); iter!=theDeque.end();++iter)
std::cout << *iter << '\n';
}
toEmplace
toPush
------
toEmplace
toPush
theEnd
Einfügung an beliebiger Stelle
Das Einfügen von Elementen in die Container vector, deque und list an beliebiger Stelle erfolgt mit den Methoden insert() und emplace(). Lediglich der Container forward_list verwendet abweichende Methoden mit dem Zusatz _after. Diese Methoden fügen das neue Element nach der angegebenen Position ein. Die Methoden liefern als Returnwert einen Iterator auf das (erste) eingefügte Datum.
Der Unterschied zwischen emplace() und insert() besteht darin, dass emplace() das einzufügende Datum instanziiert und dann einfügt während insert() ein bestehendes Datum einfügt. Aus diesem Grund kann mit emplace() nur ein Datum einfügt werden während mit insert() das Einfügen von mehreren Daten möglich ist.
Die Methoden emplace() und insert() können für die Container vector und deque unter Umständen einige Zeit in Anspruch nehmen, da die Elemente nach der Einfügeposition unter Umständen umkopiert werden müssen. In diesem Fall werden eventuelle Iteratoren ungültig. Werden häufig Elemente eingefügt oder gelöscht, sollte ein list oder forward_list Container verwendet werden.
iter insert (citer pos, const DTYP& value);
iter insert_after (citer pos, const DTYP& value);
Fügt das Datum value an/nach der Position pos ein.
iter insert (citer pos, size_type count,
const DTYP& value );
iter insert_after (citer pos, size_type count,
const DTYP& value);
Fügt an/nach der Position pos count Elemente des Datums value ein.
iter insert (citer pos, iiter first, iiter last);
iter insert_after (citer pos, iiter first, iiter last);
Fügt an/nach der Position pos die Elemente aus dem Bereich [first...last) ein.
iter insert (citer pos, ilist args);
iter insert_after (citer pos, ilist args);
Fügt an/nach der Position pos die Elemente aus der Initialisiererliste args ein.
iter emplace (citer pos, plist args);
iter emplace_after (citer pos, plist args);
Erstellt ein neues Objekt und fügt es an/nach der Position pos in den Container ein. args ist eine Auflistung der Parameter eines Konstruktors des einzufügenden Objekts.
#include <print>
#include <vector>
#include <forward_list>
#include <string>
using namespace std::string_literals;
import CData4;
int main()
{
// vector und forward_list definieren/initialisieren
std::vector<int> theVector {1,2,3,4};
std::forward_list<CData4> theFList {{"eins"s},{"zwei"s},{"drei"s}};
// Einzufuegendes CData4-Objekt definieren
CData4 obj("neu");
std::println("Inhalt Vektor:");
for(auto elem: theVector)
std::print("{}, ",elem);
std::println("\nInhalt FList: ");
for(auto elem: theFList)
std::print("{}, ",elem);
// Iterator auf 2. Element des Vektors
auto viter = theVector.begin();
++viter;
// Neue Elemente in Vetor einfuegen
theVector.insert(viter,{10,11,12});
std::println("\nNeuer Inhalt Vektor: ");
for(auto elem: theVector)
std::print("{}, ",elem);
// Drei CData4 Objekte ab 2. Position in
// forward_list einfuegen
theFList.insert_after(theFList.begin(),3,obj);
std::println("\nNeuer Inhalt FList: ");
for(auto elem: theFList)
std::print("{}, ",elem);
}
Inhalt Vektor:
1, 2, 3, 4,
Inhalt FList:
eins, zwei, drei,
Neuer Inhalt Vektor:
1, 10, 11, 12, 2, 3, 4,
Neuer Inhalt FList:
eins, neu, neu, neu, zwei, drei,
Löschen am Anfang
Enthält ein Container Zeiger, ist Vorsicht geboten. Da beim Entfernen eines Elements lediglich der Zeiger gelöscht wird, ist vorher eventuell das Objekt, auf das der Zeiger verweist, explizit zu löschen.
void pop_front();
Löscht in den Containern deque, list und forward_list das erste Element des Containers.
Löschen am Ende
void pop_back();
Löscht das letzte Element im Container vector, deque und list.
Löschen an beliebiger Stelle
Das Löschen von Elementen in den Containern vector, deque und list an einer Position erfolgt mit der Methode erase(). Auch hier verwendet der Container forward_list eine abweichende Methode mit dem Zusatz _after. Diese Methode löscht das nächste Element nach der angegebenen Position.
Beide Methoden liefert als Returnwert einen Iterator auf das nach dem gelöschten Element folgende Element.
iter erase (citer pos );
iter erase_after (citer pos);
Löscht das Element an/nach der Position pos.
iter erase (citer first, citer last );
iter erase_after (citer first, citer last );
Löscht die Elemente [first...last) bzw. (first...last). D.h., bei einer forward_list wird das Element first nicht gelöscht.
Weitere Methoden
void clear();
Löscht alle Elemente in den Containern vector, deque, list und forward_list.
void resize (size_type count [, const DTYP& value] );
Setzt die Größe des Containers vector, deque, list und forward_list auf count Elemente. Ist count kleiner als die aktuelle Anzahl der Elemente im Container, werden die überzähligen Elemente gelöscht. Ist count größer als die aktuelle Anzahl der Elemente und value nicht nicht angegeben, werden die hinzugefügten Elemente mit dem Standardkonstruktor bzw. mit 0 initialisiert. Ist value angegeben, werden die die hinzugefügten Elemente mit dem Datum value initialisiert.
void swap (CTYP& other);
Vertauscht den Inhalt von zwei Containern. Die zu vertauschenden Container müssen den gleichen Datentyp besitzen, können aber eine unterschiedliche Anzahl von Elementen enthalten.
#include <print>
#include <vector>
#include <string>
using namespace std::string_literals;
import CData4;
int main()
{
std::vector<CData4> vect {{"eins"s},{"zwei"s},{"drei"s}};
for (auto elem: vect)
std::print("{}, ",elem);
// Vektor vergroeßern, neue Elemente mit "XX" init.
std::println("\nVektor auf 5 vergroessert: ");
vect.resize(5,{"XX"s});
for (auto elem: vect)
std::print("{}, ",elem);
// Vektor verkleinern
std::println("\nVektor auf 2 verkleinert: ");
vect.resize(2);
for (auto elem: vect)
std::print("{}, ",elem);
}
eins, zwei, drei,
Vektor auf 5 vergroessert:
eins, zwei, drei, XX, XX,
Vektor auf 2 verkleinert:
eins, zwei,
Elementzugriff
Außer über Iteratoren, range-for-Schleifen oder Ranges kann mit folgenden Methoden auf die im Container abgelegten Elemente zugegriffen werden:
DTYP& operator[] (size_type pos);
Liest/beschreibt das Element an der Position pos in den Containern vector, deque und array. Das erste Element im Container hat die Position 0. Eine Bereichsüberprüfung findet nicht statt.
DTYP& at (size_type pos);
Liest/beschreibt das Element an der Position pos in den Containern vector, deque und array, wobei das erste Element im Container die Position 0 hat. Bei einer ungültigen Position wird eine Ausnahme vom Typ out_of_range ausgelöst.
DTYP& front();
Liest/beschreibt das erste Element im Container. Dieser Zugriff ist bei allen sequenziellen Containern möglich.
DTYP& back();
Liest/beschreibt das letzte Element im Container. Dieser Zugriff ist bei allen sequenziellen Containern möglich, mit Ausnahme der forward_list.
DTYP* data();
Liefert für die Container array und vector einen Zeiger auf das interne Datenfeld.
Werden in einem vector Daten eingefügt, kann danach der Zeiger ungültig werden, da eventuell neuer Speicher angefordert wurde.
#include <print>
#include <vector>
int main()
{
// Vektor definieren und intilisieren
std::vector<int> vect {1,2,3};
// Zugriff auf Elemente
try
{
// Indizierte Zugriff
std::println("2. Element: {}",vect[1]);
// Zugriff auf 3. Element ueber Iterator
auto iter = vect.begin();
std::println("3. Element: {}", *(iter+2));
// Versuch, das 10. Element zu beschreiben
// Loest eine Ausnahme aus
vect.at(9) = 99;
}
catch (std::out_of_range& ex)
{
std::println("FEHLER: {}",ex.what());
}
// Zugriff ueber front()
std::println("1. Element: {}", vect.front());
}
2. Element: 2
3. Element: 3
FEHLER: vector::_M_range_check: __n (which is 9) >= this->size() (which is 3)
1. Element: 1
Containerkapazität
Folgende Methoden liefern Informationen über die Größe bzw Inhalts eines Containers:
bool empty();
Liefert true zurück, wenn der Container keine Elemente enthält.
size_type max_size();
Liefert die maximal mögliche Anzahl von Elementen im Container zurück.
size_type size();
Liefert für alle sequenziellen Container, außer der forward_list, die Anzahl der im Container abgelegten Elemente zurück.
size_type capacity();
Liefert für den Container vector die Anzahl der Elemente zurück, die im Container abgelegt werden können, ohne dass dafür neuer Speicher angefordert wird.
void reserve (size_type anz);
Reserviert für den Container vector Speicher für anz Elemente. anz muss größer/gleich dem von capacity() zurückgelieferten Wert sein. Durch geschickte Anwendung von reserve() kann das Anfordern von Speicher beim Hinzufügen von Elementen minimiert werden, denn es wird erst dann Speicher angefordert, wenn size() größer capacity() wird.
void shrink_to_fit();
Gibt für die Container vector und deque den durch Löschen von Elementen nicht mehr belegten Speicher frei. D.h. nach der Ausführung von shrink_to_fit() ist size() gleich capacity().
#include <print>
#include <vector>
int main()
{
// Vektor mit 10 Elementen definieren
std::vector<int> vect(10);
// Anzahl der Elemente und Groesse ausgeben
std::println("Elemente:{}, Kapazitaet:{}",
vect.size(),vect.capacity());
// Ein Element an Vektor anhaengen
std::println("Fuege 1 Element an");
vect.push_back(88);
std::println("\tElemente:{}, Kapazitaet:{}",
vect.size(),vect.capacity());
// Nicht benoetigten Speicher freigeben
std::println("Nicht belegten Speicher freigeben");
vect.shrink_to_fit();
std::println("\tElemente:{}, Kapazitaet:{}",
vect.size(),vect.capacity());
}
Elemente:10, Kapazitaet:10
Fuege 1 Element an
Elemente:11, Kapazitaet:20
Nicht belegten Speicher freigeben
Elemente:11, Kapazitaet:11
Containeroperationen
Die assign() Methoden ersetzen den Inhalt eines Containers und sind für alle sequenziellen Container definiert.
void assign (size_type count, const DTYP& value);
Ersetzt den Inhalt des Containers durch count Kopien des Datums value.
void assign (iiter first, iiter last);
Ersetzt den Inhalt des Containers durch Kopien der Elemente im Bereich [first...last).
void assign (ilist args);
Ersetzt den Inhalt des Containers durch die Elemente aus der Initialisiererliste args.
Spezielle list-Operationen
Für die Containertypen list und forward_list sind weitere Methoden definiert, um Elemente im Container zu verarbeiten.
Elemente sortieren
void sort();
Sortiert die Elemente der Liste aufsteigend durch Aufruf des Operators <.
void sort (BPRED cmp);
Sortiert die Elemente durch Aufruf des binären Predicates cmp. Liefert das Predicate true zurück, wird das im ersten Parameter übergebene Datum vor dem im zweiten Parameter übergebenen Datum einsortiert.
void reverse();
Kehrt die Reihenfolge der Elemente um.
Elemente löschen
void unique();
Entfernt bis auf das erste Element unmittelbar aufeinanderfolgende gleichwertige Elemente unter Verwendung des Operators ==.
void unique (BPRED cmp);
Entfernt bis auf das erste Element unmittelbar aufeinanderfolgende gleichwertige Elemente durch Aufruf des binären Predicates cmp. Liefert das Predicate true zurück, werden die beiden übergebenen Elemente als gleichwertig angesehen und das zweite Element aus der Liste entfernt.
void remove (const DTYP& value);
Entfernt alle Elemente mit dem Datum value.
void remove_if (UPRED cmp);
Ruft für jedes Element das unäre Predicate cmp auf. Liefert das Predicate true zurück, wird das Element aus der Liste entfernt.
#include <print>
#include <list>
int main()
{
// Liste initialisieren
std::list<int> theList{41, 67, 34, 0, 69, 34, 78, 0, 62, 64};
std::println("Liste:");
for (auto& elem: theList)
std::print("{}, ",elem);
// Liste sortieren
std::println("\nListe sortiert:");
theList.sort();
for (auto elem: theList)
std::print("{}, ",elem);
// Mehrfache Werte entfernen
std::println("\nMehrfache Werte entfernt:");
theList.unique();
for (auto elem: theList)
std::print("{}, ",elem);
// Mehrfache 10er-Werte entfernen
// mittels Lambda-Funktion
std::println("\nMehrfache 10er-Wert entfernt:");
theList.unique([] (int val1, int val2)
{ return ((val1/10)==(val2/10));} );
for (auto elem: theList)
std::print("{}, ",elem);
// Alle ungerade Werte entfernen
std::println("\nUngerade Werte entfernt:");
theList.remove_if([] (int val)
{ return val&0x01; });
for (auto elem: theList)
std::print("{}, ",elem);
}
Liste:
41, 67, 34, 0, 69, 34, 78, 0, 62, 64,
Liste sortiert:
0, 0, 34, 34, 41, 62, 64, 67, 69, 78,
Mehrfache Werte entfernt:
0, 34, 41, 62, 64, 67, 69, 78,
Mehrfache 10er-Wert entfernt:
0, 34, 41, 62, 78,
Ungerade Werte entfernt:
0, 34, 62, 78,
Denken Sie daran, dass ein Predicate entweder eine (Template-)Funktion, ein Funktionsobjekt oder ein Lambda-Ausdruck sein kann.
Elemente verschieben
Die splice() Methoden verschieben Elemente aus einer Liste in eine andere Liste.
void splice (citer pos, list& other);
Verschiebt alle Elemente aus der Liste other in die aktuelle Liste vor die Position pos. Die Liste other ist danach ein leer.
void splice (citer pos, list& other, citer spos );
Verschiebt das Element an der Position spos aus der Liste other in die aktuelle Liste vor die Position pos.
void splice (citer pos, list& other, citer first, citer last);
Verschiebt die Elemente im Bereich [first...last) der Liste other in die aktuelle Liste vor die Position pos.
#include <print>
#include <list>
#include <iterator>
int main()
{
// 2 Listen definieren/initialisieren
std::list<int> lobj1 {1,2,3,4,5,6,7,8};
std::list<int> lobj2 {10,11,12,13,14};
std::print("Liste1: ");
for (auto elem: lobj1)
std::print("{},",elem);
std::print("\nListe2: ");
for (auto elem: lobj2)
std::print("{},",elem);
// Einfuegeposition definieren
// Liste1, Pos. 2 -> Wert:3
auto iter1 = lobj1.begin();
std::advance(iter1,2);
// Auszuschneidenden Bereich definieren
// Liste2, [Startpos. 1 -> Wert:11
// Endpos. 3 -> Wert 14)
// iter2last auf Wert '14'
auto iter2first = lobj2.begin();
++iter2first;
auto iter2last = iter2first;
advance(iter2last,3);
std::println("\n3 Elemente aus Liste2 ab Position 1 \n"
"nach Liste1 ab Position 2 verschoben.");
lobj1.splice(iter1,lobj2,iter2first,iter2last);
std::print("Liste1: ");
for (auto elem: lobj1)
std::print("{},",elem);
std::print("\nListe2: ");
for (auto elem: lobj2)
std::print("{},",elem);
}
Liste1: 1,2,3,4,5,6,7,8,
Liste2: 10,11,12,13,14,
3 Elemente aus Liste2 ab Position 1
nach Liste1 ab Position 2 verschoben.
Liste1: 1,2,11,12,13,3,4,5,6,7,8,
Liste2: 10,14,
Listen zusammenfügen
Die merge() Methoden vereinen zwei Listen. Beide Listen sollten aufsteigend sortiert sein.
void merge (list& other );
Fügt die Liste other in die aktuelle Liste ein. Die Liste other ist anschließend leer.
void merge (list& other, BPRED cmp);
Fügt die Liste other in die aktuelle Liste ein und verwendet als Sortierkriterium das binäre Predicate cmp. Liefert das Predicate true zurück, wird das im ersten Parameter übergebene Element vor dem im zweiten Parameter übergebene Element eingefügt.
Das nachfolgende Beispiel zeigt auf, wie zwei Listen in absteigender Reihenfolge zusammengefügt werden. Dabei ist zu beachten, dass die Listen vor dem Zusammenfügen bereits in der gewünschten Reihenfolge vorliegen.
#include <print>
#include <list>
#include <iterator>
// Sortierkriterium
bool Descend(int val1, int val2)
{
return val1>val2;
}
int main()
{
// Zwei Listen definieren/initialieren
std::list<int> list1 = { 5,9,0,1,3 };
std::list<int> list2 = { 8,7,2,6,4 };
// Listen in absteigender Reihenfolge sortieren
list1.sort(Descend);
list2.sort(Descend);
std::println("Liste1: ");
for (auto elem: list1)
std::print("{}, ",elem);
std::println("\nListe2: ");
for (auto elem: list2)
std::print("{}, ",elem);
// Und nun Listen zusammenfügen
list1.merge(list2,Descend);
std::println("\nListen zusammenfuegen\nListe1: ");
for (auto elem: list1)
std::print("{}, ",elem);
std::println("\nListe2: ");
for (auto elem: list2)
std::print("{}, ",elem);
}
Liste1:
9, 5, 3, 1, 0,
Liste2:
8, 7, 6, 4, 2,
Listen zusammenfuegen
Liste1:
9, 8, 7, 6, 5, 4, 3, 2, 1, 0,
Liste2:
Übungen
array_01:
Legen Sie ein Array zur Aufnahme von 4 CData4-Objekten an und initialisieren Sie das Array bei seiner Definition vollständig mit beliebigen Daten. Der Konstruktor der Klasse CData4 erwartet als Parameter einen string.
Die Klasse CData4 ist in der Modul-Datei cdata4.cxx definiert (siehe Anhang T: CData4 Klasse).
Geben Sie das Array aus. Die Ausgabe kann eines CData4-Objekts kann sowohl mittels cout wie auch mit print() erfolgen.
Geben Sie das Array in umgekehrter Reihenfolge aus. Verwenden Sie dazu den for_each Algorithmus und nach Möglichkeit einen entsprechenden View.
Geben Sie das 1. Element aus und versuchen danach das 10. Element zu beschreiben.
Überschreiben Sie alle Elemente des Arrays mit einem anderen beliebigen CData4-Objekt und geben das Array erneut aus.
Arrayinhalt:
eins,zwei,drei,vier,
Nun Rueckwaerts:
vier,drei,zwei,eins,
1. Element: eins
10. Element:
Fehler: invalid array
Ueberschreiben mit neuen Objekten:
alles neu,alles neu,alles neu,alles neu,
vector_01:
Die Datei aktien.csv enthält Aktienkurse von Unternehmen. Jede Zeile enthält die Daten zu einem Unternehmen in folgender Form:
Adidas;269.70;296.75;01.08.2019
Der erste Eintrag ist der Name des Unternehmens (Adidas), anschließend folgen der aktuelle Kurs (269.70), der höchste Kurs der Aktie (296.75) sowie das Datum des Höchststandes.
Lesen Sie die Daten ein, legen sie in einem Vektor ab und geben den Inhalt des Vektors wieder aus.
Suchen Sie das Unternehmen, dessen Aktie am meisten verloren hat und geben Sie den Verlust in Prozent aus.
Adidas akt: 269.70, max: 296.75 am 01.08.2019
Bayer akt: 67.34, max: 146.20 am 10.04.2015
BMW ST akt: 60.81, max: 123.75 am 17.03.2015
Daimler akt: 42.71, max: 109.39 am 07.05.1998
Deutsche Bank akt: 6.57, max: 108.14 am 14.05.2007
Deutsche Telekom akt: 15.18, max: 104.90 am 06.03.2000
Infineon akt: 15.75, max: 93.60 am 27.06.2000
Muenchener Rueck akt: 217.90, max: 397.73 am 10.11.2000
SAP akt: 108.60, max: 125.00 am 03.07.2019
Siemens akt: 90.95, max: 133.50 am 04.05.2017
Volkswagen VZ akt: 146.10, max: 262.45 am 17.03.2015
Lufthansa akt: 8.50, max: 9.20 am 04.09.2019
Groesster Verlust: Deutsche Bank 94 %
deque_01:
Es ist eine Verwaltung für Arbeitsaufträge zu erstellen, wobei die Arbeitsaufträge in der Reihenfolge abgearbeitet werden sollen, in der sie erstellt wurden.
Damit die Übung überschaubar bleibt, besteht ein Arbeitsauftrag lediglich aus einem string-Objekt, das den Arbeitsauftrag beschreibt.
Erstellen Sie 3 Arbeitsaufträge und geben Sie diese aus.
Bearbeiten Sie 2 Arbeitsaufträge und geben dann die noch offenen Arbeitsaufträge aus.
Erstellen Sie einen vierten Arbeitsauftrag und geben erneut die offenen Arbeitsaufträge aus.
Bearbeiten Sie zum Schluss noch alle offenen Arbeitsaufträge.
Anstehende Arbeiten:
Arbeitsauftrag 3
Arbeitsauftrag 2
Arbeitsauftrag 1
Bearbeite Arbeitsauftrag 1
Bearbeite Arbeitsauftrag 2
Noch offene Arbeitsauftraege:
Arbeitsauftrag 3
Neuer Arbeitsauftrag:
Noch offene Arbeitsauftraege:
Arbeitsauftrag 4
Arbeitsauftrag 3
Alle Arbeitsauftraege abarbeiten:
Bearbeite Arbeitsauftrag 3
Bearbeite Arbeitsauftrag 4
list_01:
Eine unbegrenzte Anzahl von Flugdaten, bestehend aus Abflugzeit (std/min als int-Datum), Flugnummer und Zielort (als String), ist abzuspeichern.
Legen Sie folgende 4 Flugdaten ab und geben diese zur Kontrolle aus.
10:30, X 53, Palma de Mallorca
15:15, LH 333, Alicante
08:10, AA 657, New York
08:00, GW 392, Hamburg
Sortieren Sie die Flugdaten aufsteigend nach der Abflugzeit und geben die sortierten Daten erneut aus.
Sortieren Sie die Flugdaten fallend nach Zielort und geben die sortierten Daten erneut aus.
10:30, Flug X 53 nach Palma de Mallorca
15:15, Flug LH 333 nach Alicante
08:10, Flug AA 657 nach New York
08:00, Flug GW 392 nach Hamburg
Sortiert nach Abflugzeit:
08:00, Flug GW 392 nach Hamburg
08:10, Flug AA 657 nach New York
10:30, Flug X 53 nach Palma de Mallorca
15:15, Flug LH 333 nach Alicante
Sortiert nach Zielort:
10:30, Flug X 53 nach Palma de Mallorca
08:10, Flug AA 657 nach New York
08:00, Flug GW 392 nach Hamburg
15:15, Flug LH 333 nach Alicante