Advanced Java Services | vector |
Eine vollständige Dokumentation des Templates vector findet man auf den beiden Referenzseiten
http://www.cplusplus.com/reference/vector/vector/ http://en.cppreference.com/w/cpp/container/vector
Das Template vector<T> ist ein Container für einen Typ T und stellt ein verallgemeinertes Array dar. Durch Einsetzen eines konkreten Typs wird das Template zu einer Klassse. vector<T> stellt komfortable Methoden zum Aufnehmen und Manipulieren seiner Elemente bereit, von denen einige hier vorgestellt werden.
Wie bei einem Array kann man sich bei einem Vector (im Gegensatz zu einer Deque) darauf verlassen, daß alle Elemente strikt hintereinander im Speicher stehen. Ist das im Hintergrund vorhandene Array voll, so muß ein neuer größerer zusammenhängender Speicherblock angeschafft werden und der komplette Inhalt kopiert werden, eine u.U. aufwendige Operation. Daher wird ein Vector innen mit einem größeren Array arbeiten, als die momentane Länge des Vectors zeigt. Daher ist das Einfügen am Ende ein sehr schnelle Operation. Das Einfügen am Anfang dagegen ist aufwendig, da in diesem Fall alle Elemente um einen Index nach rechts kopiert werden müssen.
#include <vector> using namespace std;
Es gibt die folgenden Iteratoren
vector::iterator vector::const_iterator vector::reverse_iterator vector::const_reverse_iterator
Konstruktoren | |
---|---|
vector<T>() |
Constructs an empty vector of type T. |
explicit vector<T>(size_type n) |
Constructs a container with n elements, each element is zero |
vector<T>(size_type n, const value_type& val, const allocator_type& alloc = allocator_type()) |
Constructs a container with n elements, each element is a copy of val |
int intArr[] = { 1, 2, 3 }; std::vector<int> intVector(intArr, intArr + sizeof(intArr) / sizeof(int)); // iterator-begin, iterator-end
int intArr[] = { 1, 2, 3 }; std::vector<int> intVector; // empty intVector.assign(intArr, intArr + sizeof(intArr) / sizeof(int)); // iterator-begin, iterator-end
std::vector<int> intVector; // empty intVector.insert(intVector.begin(), intArr, intArr + sizeof(intArr) / sizeof(int)); // einfügeposition, iterator-begin, iterator-end
Initialisierungslisten vereinfachen das Initialisieren drastisch.
std::vector<int> intVector = { 1, 2, 3, 4 };
std::vector<string> stringVector = { "fee", "fi", "fo", "fum" };
Methoden wie etwa empty(), front(), back(), pop_back(), max_size(), clear() sind intuitiv zu verwenden.
Vorgestellt werden: size(), capacity(), push_back(), Indexoperator [], at(), insert(), erase(), assign().
Elemente werden am Ende aufgenommen, es gibt kein push_front.
void beispiel1_push_back_size_capacity() { vector<int> intVector; cout << "Anzahl der Elemente: " << intVector.size() << endl; cout << "Kapazität : " << intVector.capacity() << endl; for (int i = 1; i < 11; ++i) { intVector.push_back(i*i*i); } for(int elem : intVector) cout << elem << " " ; cout <<endl; cout << "Anzahl der Elemente: " << intVector.size() << endl; cout << "Kapazität : " << intVector.capacity() << endl; }
Output
Antahl der Elemente: 0 Kapazität : 0 1 8 27 64 125 216 343 512 729 1000 Antahl der Elemente: 10 Kapazität : 16
Diese Methode ist neu in C++11. Hier wird wird ein Rvalue-Referenz übergeben, da.h. "val" symbolisiert einen temporären Speicher, der nirgends sonst gebraucht wird. Das Vectorobjekt übernimmt beim Einfügen diesen Speicher.
void beispiel1_push_back_cplusplus11() { vector<const char*> cstringVector; cstringVector.push_back("1"); cstringVector.push_back("2"); for(const char* elem : cstringVector) cout << elem << " " ; cout << endl; }
Um den Aufruf zu dokumentieren manipulieren wir den Header stl_vector.h von MinGW wie folgt: Etwa um die Zeile 930 findet man folgende Codezeilen
#if __cplusplus >= 201103L void push_back(value_type&& __x) { emplace_back(std::move(__x)); } templatevoid emplace_back(_Args&&... __args); #endif
und fügt im Funktionsrumpf eine Konsolmeldung ein
#if __cplusplus >= 201103L void push_back(value_type&& __x) { printf("push_back(value_type&& __x)\n"); emplace_back(std::move(__x)); } templatevoid emplace_back(_Args&&... __args); #endif
Damit erhält man folgenden Output
push_back(value_type&& __x) push_back(value_type&& __x) 1 2
Der Indexoperator ermöglicht einen Arrayzugriff.
void beispiel_index_operator() { vector<double> doubleVector = { 1, 2, 3, 4, 5 }; cout << "Anzahl der Elemente: " << doubleVector.size() << endl; cout << "Kapazität : " << doubleVector.capacity() << endl; for(int elem : doubleVector) cout << elem << " " ; cout << endl; cout << "doubleVector[3] = " << doubleVector[3] << endl; // 4 doubleVector[3] = 42; cout << "doubleVector[3] = " << doubleVector[3] << endl; // 4 //cout << doubleVector[2000] << endl; // kein warning // programmabbruch zur laufzeit }
Output
1 2 3 4 5 doubleVector[3] = 4 Anzahl der Elemente: 5 Kapazität : 5 doubleVector[3] = 42
Leider wird ein falscher Arrayzugriff nicht überprüft und führt daher zur Laufzeit zu einem Programmabruch. Abhilfe schafft hier die nächste Methode.
Die Methode at() erlaubt exakt wie der Indexoperator einen lesenden und schreibenden Zugriff. Hier wird allerdings der Bereich überprüft. Ein falscher Arrayzugriff führt zur Laufzeit zu einer Exception vom Typ out_of_range, die man mit try catch behandeln kann.
// zugriff mit at() // vorteil: rangecheck ! cout << "doubleVector.at(4) = " << doubleVector.at(4) << endl; doubleVector.at(4) = 55; cout << "doubleVector.at(4) = " << doubleVector.at(4) << endl; //cout << "doubleVector.at(5) = " << doubleVector.at(5) << endl; //This application has requested the Runtime to terminate it in an unusual way. //Please contact the application's support team for more information. // out_of_range -> logic_error -> exception <stdexcept> try { cout << "doubleVector.at(5) = " << doubleVector.at(5) << endl; } catch(out_of_range& ex) // catching by reference is recommended { cout << ex.what() << endl; //vector::_M_range_check: __n (which is 5) >= this->size() (which is 5) }
C++11 hat zwei neue Überladungen hinzugefügt, sodaß es jetzt 5 Überladungen dieser Methoden gibt. Alle insert-Methoden arbeiten mit Iteratoren. Man kann an beliebigen (existierenden) Positionen einfügen. Man kann einzelne Elemente einfügen oder auch mehrere.
Einzelnes Element einfügen
cout << "insert" << endl; // einzelnes element //iterator insert (iterator position, const value_type& val); // vector von c-strings std::vector<char*> c_stringVector = { "eins", "zweins", "vierns" }; c_stringVector.insert( c_stringVector.begin()+ 2, "dreins"); for(char* elem : c_stringVector) cout << elem << " " ; cout << endl;
Output
eins zweins dreins vierns
Mehrmals dasselbe Element einfügen
std::vector<char*> c_stringVector2 = { "a", "c" }; c_stringVector2.insert( c_stringVector2.begin()+ 1, 5, "b"); for(char* elem : c_stringVector2) cout << elem << " " ; cout << endl;
Output
a b b b b b c
Einen anderen Vector einfügen
std::vector<int> intVector(3, 5); // 3 mal die 5 // einfügeposition, iterator-begin, iterator-end std::vector<double> doubleVector = { 1.1, 3.3 }; doubleVector.insert(doubleVector.end(), intVector.begin(), intVector.end()); for(double elem : doubleVector) cout << elem << " " ; cout << endl;
Output
1.1 3.3 5 5 5
Ein Array einfügen
int intArr[] = { 17, 17 }; // // einfügeposition, iterator-begin, iterator-end std::vector<double> doubleVector2 = { 1.1, 3.3 }; doubleVector.insert(doubleVector2.begin(), intArr, intArr + sizeof(intArr) / sizeof(int) ); for(double elem : doubleVector) cout << elem << " " ; cout << endl;
Output
1.1 3.3 5 5 5
Eine std::list in einen Vector einfügen
std::list<int> intList = {17, 42, 42, 17}; // C++11 std::vector<double> doubleVector3 = { 2.2, 4.4 }; doubleVector3.insert(doubleVector3.begin() + 1, std::begin(intList), std::end(intList) ); for(double elem : doubleVector3) cout << elem << " " ; cout << endl;
Hinweis: man kann in dieser Situation keinen Listiterator verwenden
Output
2.2 17 42 42 17 4.4
Beispiel für den Aufruf der C++11 Methode iterator insert (const_iterator position, value_type&& val);
std::vector<const char*> charpoiVector = { "foo", "bar"}; charpoiVector.insert(charpoiVector.begin() + 1 , "foobar"); for(const char* elem : charpoiVector) cout << elem << " " ; cout << endl;
Um den Aufruf zu dokumentieren manipulieren wir den Header stl_vector.h von MinGW wie folgt: Etwa um die Zeile 1016 findet man folgende Codezeilen
iterator insert(const_iterator __position, value_type&& __x) { return emplace(__position, std::move(__x)); }
wo man analog zum Beispiel mit push_back auch eine Konsolomeldung einträgt
iterator insert(const_iterator __position, value_type&& __x) { printf("insert(const_iterator __position, value_type&& __x)\n"); return emplace(__position, std::move(__x)); }
Damit ergibt sich folgender Output
insert(const_iterator __position, value_type&& __x) foo foobar bar
Rs gibt zwei erase()-Methoden. Beide arbeiten mit Iteratoren.
iterator erase (const_iterator position); iterator erase (const_iterator first, const_iterator last); // gelöscht wir von first bis zu last - 1 !!!
Das Beispiel zeigt beide Methoden.
void beispiel_erase() { vector<int> intVector = { 1, 3, 5, 7, 9, 11, 13 }; // C++11 // einzelnes element löschen, letztes element löschen intVector.erase(intVector.end() - 1); for(int elem: intVector) cout << elem << " " ; cout << endl; // 1 3 5 7 9 11 // erstes element löschen intVector.erase(intVector.begin()); for(int elem: intVector) cout << elem << " " ; cout << endl; // 3 5 7 9 11 //bereich löschen // intVector.erase(intVector.begin() + 1 , intVector.end() - 1); for(int elem: intVector) cout << elem << " " ; cout << endl; // 3 11 }
Es gibt drei assign()-Methoden.
(1) void assign (InputIterator first, InputIterator last); (2) void assign (size_type n, const value_type& val); (3) void assign (initializer_listil); // C++11
Das Beispiel zeigt alle drei Methoden.
void beispiel_assign() { cout << "beispiel5_assign" << endl; cout << "-------------------------" << endl; // verschiedene syntax für initialisierungslisten vector<int> intVectorA = { 1, 3 , 5, 7, 9 }; // C++11 vector<int> intVectorB{ 1, 3 , 5, 7, 9 }; // C++11 es geht auch ohne = !!! vector<int> intVectorC( { 1, 3 , 5, 7, 9 } ); // C++11 es geht auch ohne = !!! // weil es einen c'tor gibt der eine initialisierungsliste bekommt !!! // (1) alte werte werden gelöscht ! vector<int> intVector = { 1, 3 , 5, 7, 9 }; // C++11 int intArr[] = {2, 4, 6, 8, 10}; cout << "intVector.size = " << intVector.size() << endl; intVector.assign( intArr +1, intArr + sizeof(intArr)/sizeof(int) -1 ); for(int elem : intVector) cout << elem << " " ; cout << endl; cout << "intVector.size = " << intVector.size() << endl; cout << "-------------------------" << endl; // (2) alte werte werden gelöscht ! vector<int> intVector2 = { 1, 3 }; // C++11 intVector2.assign(7, 17); // 7-mal die 17 for(int elem : intVector2) cout << elem << " " ; cout << endl; cout << "intVector2.size = " << intVector2.size() << endl; cout << "-------------------------" << endl; // (3) alte werte werden gelöscht ! vector<int> intVector3 = { 2, 4 }; // C++11 intVector3.assign( { 1, 3, 5 } ); // C++11 for(int elem : intVector3) cout << elem << " " ; cout << endl; cout << "intVector3.size = " << intVector3.size() << endl; }
Ausgabe
beispiel_assign ------------------------- intVector.size = 5 4 6 8 intVector.size = 3 ------------------------- 17 17 17 17 17 17 17 intVector2.size = 7 ------------------------- 1 3 5 intVector3.size = 3