Advanced   Java   Services vector



std::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 und Namespace
#include <vector>
using namespace std;

Iteratoren

Es gibt die folgenden Iteratoren

vector::iterator
vector::const_iterator
vector::reverse_iterator
vector::const_reverse_iterator

Konstruktoren
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

Vector aus C-Array erzeugen


Mit Hilfe eines Konstruktors
int intArr[] = { 1, 2, 3 };
std::vector<int> intVector(intArr, intArr + sizeof(intArr) / sizeof(int));
// iterator-begin, iterator-end

Mit der Methode assign()
int intArr[] = { 1, 2, 3 };
std::vector<int> intVector; // empty
intVector.assign(intArr, intArr + sizeof(intArr) / sizeof(int));
// iterator-begin, iterator-end

Mit der Methode insert()
std::vector<int> intVector; // empty
intVector.insert(intVector.begin(), intArr, intArr + sizeof(intArr) / sizeof(int));
// einfügeposition, iterator-begin, iterator-end

Initialisierungslisten  (C++11)

Initialisierungslisten vereinfachen das Initialisieren drastisch.

std::vector<int> intVector = { 1, 2, 3, 4 };
std::vector<string> stringVector = { "fee", "fi", "fo", "fum" };

Zugriff auf die Elemente und einfache Abfragemethoden

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().


push_back(), size(), capacity()

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

push_back(value_type&& val)

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)); }

      template
        void
        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));
      }

      template
        void
        emplace_back(_Args&&... __args);
#endif

Damit erhält man folgenden Output

push_back(value_type&& __x)
push_back(value_type&& __x)
1  2

Indexoperator []

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.


at()

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)
}






insert()

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

erase()

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
}

assign()

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_list il);   // 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