In einem vector wird eine (fast) beliebige Menge von Elementen
so gespeichert, daß der Zugriff über einen Index (im Bereich von 0 bis
(Anzahl der Elemente-1)) effektiv möglich ist. Dies kann
über den Operator []
erfolgen, sodaß die Verwendung einem
üblichen (eindimensionalen) Feld sehr ähnlich ist.
Allerdings ist die Anzahl der in einem vector gespeicherten Elemente zur Laufzeit einstellbar.
Dabei muß man allerdings beachten, daß die Feldgröße nicht durch bloße Verwendung eines Index wächst, sondern nur durch eine Anfangsgröße beim Erzeugen, durch explizites Einfügen oder Verlängern gesetzt wird (im Gegensatz zu der in Felder beliebigen Typs mit variablen Grenzen vorgestellten Lösung).
Beim Zugriff auf einzelne Elemente mit dem Operator []
wird
-ebenso wie bei gewohnten Feldern- keine
Feldgrenzenüberprüfung durchgeführt. Alternativ kann man mit der
Methode at( size_t index )
eine Referenz auf das Element an der
Position index erhalten; dabei wird die Einhaltung der aktuellen
Feldgrenzen geprüft und im Fehlerfall eine Ausnahme out_of_range
geworfen (siehe Ausnahmen der Standardbibliothek).
Ein kleines Beispiel für die Verwendung von vector erzeugt ein Feld von int-Werten:
// Time-stamp: "23.12.02 15:50 vector1.cpp klaus@wachtler.de" // // Demonstriert die Grundfunktionlität von vector<class T>. // // Y:\>cl /GX /W0 vector1.cpp & vector1 // v[0] = 10 // v[1] = 11 // v[2] = 0 // v[3] = 0 // v[4] = 0 // v[5] = 12 // v[6] = 13 // Element an der Stelle 10 = 49 // (hat geklappt) // Fehler in main() entdeckt: // invalid vector<T> subscript // Element an der Stelle 10 = -5 // (hat geklappt) #include <iostream> #include <vector> #include <stdexcept> using namespace std; int main( int nargs, char **args ) { vector<int> v( 5 ); // 5 Elemente als Anfangsgröße try { v.push_back( 12 ); // ein (sechstes) Element anhängen v.push_back( 13 ); // ein (siebtes) Element anhängen v[0] = 10; // ein Element verwenden (ohne Feldgrenzenüberprüfung) v.at( 1 ) = 11; // ein Element verwenden (mit Feldgrenzenüberprüfung) // alle Elemente ausgeben: for( unsigned i=0; i<v.size(); i++ ) { cout << "v[" << i << "] = " << v[i] << endl; } // (vermutlich) ungestraft hinter das Feldende fassen: cout << "Element an der Stelle 10 = " << v[10] << endl; cout << "(hat geklappt)" << endl; // Ebenfalls hinter das Feldende fassen; aber hier kommt eine Ausnahme: cout << "Element an der Stelle 10 = " << v.at( 10 ) << endl; cout << "(hat geklappt)" << endl; } catch( exception &fehler ) { cerr << "Fehler in main() entdeckt:" << endl; cerr << fehler.what() << endl; } catch( ... ) { cerr << "Unbekannter Fehler in main() entdeckt" << endl; } try { // Feld verlängern, dann kommt auch keine Ausnahme! // auf 11 Elemente (0..10) verlängern, neue Werte stehen auf -5: v.resize( 11, -5 ); cout << "Element an der Stelle 10 = " << v.at( 10 ) << endl; cout << "(hat geklappt)" << endl; } catch( exception &fehler ) { cerr << "Fehler in main() entdeckt:" << endl; cerr << fehler.what() << endl; } catch( ... ) { cerr << "Unbekannter Fehler in main() entdeckt" << endl; } }
Die Ausgabe dieses Programms ist:
v[0] = 10 v[1] = 11 v[2] = 0 v[3] = 0 v[4] = 0 v[5] = 12 v[6] = 13 Element an der Stelle 10 = 134529592 (hat geklappt) Fehler in main() entdeckt: vector [] access out of range Element an der Stelle 10 = -5 (hat geklappt)
Ein weiteres Beispiel zeigt neben der Erzeugung des Vektorinhalts auch die üblichen Möglichkeiten, über die vorhandenen Elemente zu iterieren:
// Time-stamp: "30.10.04 10:52 vec.cpp klaus@wachtler.de" // // demonstriert std::vector // // 25.10.2004 kw unter Linux entworfen und getestet, mit VC6 nicht // kompilierbar // // 27.10.2004 kw Mit VC6 (cl 12.00.8168) getestet unter Windows 2000 // Übersetzen: cl /EHsc vec.cpp // // 29.10.2004 kw Getestet mit MS VC++ 7.1 (VCToolkit), // cl-Version 13.10.3077 unter Windows 2000 // Übersetzen: cl /EHsc vec.cpp // // Außerdem mit devcpp (g++ 3.3.1 mingw) getestet // unter Windows 2000. // Übersetzen mit g++ -Wall vec.cpp -o vec.exe // // Nach wie vor unter Linux mit g++ 3.3.4 lauffähig // (g++ -Wall vec.cpp -o vec) // // 30.10.2004 kw demo_foreach_fo() eingefügt // #include <iostream> #include <vector> #include <stdexcept> #include <algorithm> // Gibt einen beschreibenden (text) und anschließend einen vector // Element für Element aus: template<typename T> void zeigevector( const char *text, std::vector<T> v ) { std::cout << "vector\n" << text << std::endl; for( size_t i=0; i<v.size(); i++ ) { std::cout << "[" << i << "] = " << v[i] << std::endl; } std::cout << std::endl; } std::vector<int> demo_simple() { size_t i; // vector aus int-Werten: std::vector<int> vint(5); // Anfangslänge 5 for( i=0; i<vint.size(); i++ ) { vint[i] = i*i; } zeigevector( "vint anfangs", vint ); try { // Das Element an der Stelle 5 ist unzulässig! // Mit [5] kann trotzdem zugegriffen werden (auch auf die Gefahr // hin, daß das Programm Unsinn macht): std::cout << "vint[5] = " << vint[5] << std::endl; // Hier wird dagegen zwar ebenfalls versucht, auf ein unzulässiges // Element zuzugreifen; aber bei at() wird dies zur Laufzeit // erkannt und eine Ausnahme geworfen: std::cout << "vint.at(5) = " << vint.at(5) << std::endl; } catch( std::exception &Fehler ) { std::cerr << "Fehler: <" << Fehler.what() << "> in demo_simple()\n"; } catch( ... ) { std::cerr << "unbekannter Fehler in demo_simple()\n"; } try { // Wenn man ein Element anhängt, geht alles klar: vint.push_back( 99 ); // verlängert um ein Element std::cout << "vint[5] = " << vint[5] << std::endl; std::cout << "vint.at(5) = " << vint.at(5) << std::endl; } catch( std::exception &Fehler ) { std::cerr << "Fehler: <" << Fehler.what() << "> in demo_simple()\n"; } catch( ... ) { std::cerr << "unbekannter Fehler in demo_simple()\n"; } // Den vector etwas verlängern: vint.resize( 10 ); for( i=5; i<vint.size(); i++ ) { vint[i] = i*i; } // neuen Inhalt ausgeben: zeigevector( "vint nach Verlängern", vint ); return vint; } // zeigt alle Elemente, Zugriff mit [] void demo_index( std::vector<int> vint ) { std::cout << "demo_index:" << std::endl; // normale Schleife über alle Elemente: size_t i; for( i=0; i<vint.size(); i++ ) { std::cout << "vint[" << i << "] = " << vint[i] << std::endl; } std::cout << std::endl; } // zeigt alle Elemente, Zugriff mit at() void demo_at( std::vector<int> vint ) { std::cout << "demo_at:" << std::endl; // normale Schleife über alle Elemente: size_t i; for( i=0; i<vint.size(); i++ ) { std::cout << "vint.at( " << i << " ) = " << vint.at( i ) << std::endl; } std::cout << std::endl; } // zeigt alle Elemente, Zugriff mit einem Iterator void demo_iterator( std::vector<int> vint ) { std::cout << "demo_iterator:" << std::endl; // Iteration über alle Elemente: std::vector<int>::iterator i; for( i=vint.begin(); i!=vint.end(); i++ ) { std::cout << "*i" << " = " << *i << std::endl; } std::cout << std::endl; } // zeigt alle Elemente, Zugriff mit for_each und einer Funktion // gibaus(): void gibaus( int i ) { std::cout << "i" << " = " << i << std::endl; }
void demo_foreach_fkt( std::vector<int> vint ) { std::cout << "demo_foreach_fkt:" << std::endl; // Schleife aus <algorithm> über alle Elemente; was mit jedem Objekt // zu geschehen hat, wird von der Funktion gibaus() definiert: std::for_each( vint.begin(), vint.end(), gibaus ); std::cout << std::endl; } // zeigt alle Elemente, Zugriff mit for_each und einem Funktionsobjekt // der Klasse gib: // Ein Objekt dieser Klasse kann erstens mit einer int als Parameter // aufgerufen werden (und gibt dann das Element aus), und zweitens // werden diese Aufrufe mitgezählt und die Anzahl kann mit // getAnzahlAufrufe() abgefragt werden. class gib { public: // Konstruktor gib() : anzahlAufrufe( 0 ) {} // Mit diesem Operator kann ein Objekt o dieser Klasse wie eine // Funktion mit einem int-Parameter aufgerufen werden, z.B.: // o( 5 ); void operator()( const int &aktuellesObjekt ) { std::cout << "aktuellesObjekt = " << aktuellesObjekt << std::endl; anzahlAufrufe++; } // liefert zurück, wieoft operator()( const int &aktuellesObjekt ) // schon für das aktuelle Objekt (*this) aufgerufen wurde: int getAnzahlAufrufe() { return anzahlAufrufe; } private: int anzahlAufrufe; }; // class gib... void demo_foreach_fo( std::vector<int> vint ) { std::cout << "demo_foreach_fo" << std::endl; // Schleife aus <algorithm> über alle Elemente. // Was mit jedem Objekt zu geschehen hat, wird von dem // Funktionsobjekt fo definiert: gib fo; // Funktionsobjekt, das als fo( int ) verwendet werden kann // (also wie eine Funktion, durch den operator() in gib). // Die Anzahl der Aufrufe wird intern mitgezählt. // Wenn man den Rückgabewert von for_each() nicht verwendet, kann // man anschließend nicht mehr die Anzahl der in fo gespeicherten // Aufrufe feststellen, weil fo an for_each mit call by referene // übergeben wird, also eine Kopie angelegt wird. In dieser Kopie // werden dann die Aufrufe gezählt; der Zähler in fo bleibt auf 0 // stehen. Glücklicherweise liefert for_each() aber seine Kopie // wieder als Rückgabewert zurück; daher die Zuweisung an fo: fo = std::for_each( vint.begin(), vint.end(), fo ); std::cout << std::endl; // Wieviele Elemente wurden ausgegeben? std::cout << "Es wurden " << fo.getAnzahlAufrufe() << " Elemente ausgegeben." << std::endl; } int main( int nargs, char **args ) { std::vector<int> v; v = demo_simple(); demo_index( v ); demo_at( v ); demo_iterator( v ); demo_foreach_fkt( v ); demo_foreach_fo( v ); return 0; } // main( int nargs, char **args )
Dazu ein paar Anmerkungen. Die Iteration über alle Elemente erfolgt:
[
...]
) wie bei einem
einfachen Feld auch (ohne Überprüfung der Feldgenzen)
[
...]
wird das jeweilige Element mit
at(...) ausgewählt; dabei wird die Einhaltung der
Feldgrenzen überprüft
std::for_each
aus <algorithm>
,
der eine Sequenz übergeben wird (für den gesamten Container also
begin() und end()) sowie eine
Funktion gibaus()5.1, welche angibt,
was mit jedem Element zu geschehen
hat (siehe Algorithmen).
std::for_each
,
allerdings nicht durch Übergabe einer Funktion, sondern eines
Funktionsobjekts stattdessen. Dabei wird ausgenutzt, daß
std::for_each
ohnehin nur ein template
ist und
letztlich vom Compiler mit einer Art Textersetzung ausgewertet
wird.
Innerhalb dieses template wird der dritte Parameter
verwendet, indem ihm das jeweils aktuelle Element (hier: eine int) des Vektors wie
bei einem Funktionsaufruf übergeben wird. Dadurch muß dieser dritte
Parameter an std::for_each
nicht unbedingt wirklich eine
Funktion sein, sondern man kann auch ein Objekt übergeben, für das
der operator()( int ) passend überladen ist.
Diese Vorgehensweise sieht auf den ersten Blick ziemlich umständlich aus, eröffnet aber leicht weitere Möglichkeiten. Im Beispiel ist in dem übergebenen Objekt noch ein Zähler eingebaut, in dem die bisherige Anzahl der Aufrufe gespeichert wird. Nach dem Ende der Schleife kann dann die Anzahl der Aufrufe und damit der ausgegebenen Elemente festgestellt werden.
Funktionsobjekte haben also die Möglichkeiten, neben ihrer eigentlichen Funktionalität (hier: die Ausgabe eines Elements) noch weitere Informationen zu tragen.
Siehe auch Funktionsobjekte.
Die Ausgabe des Programms ist dementsprechend:
vector vint anfangs [0] = 0 [1] = 1 [2] = 4 [3] = 9 [4] = 16 vint[5] = 0 Fehler: <vector [] access out of range> in demo_simple() vint[5] = 99 vint.at(5) = 99 vector vint nach Verlängern [0] = 0 [1] = 1 [2] = 4 [3] = 9 [4] = 16 [5] = 25 [6] = 36 [7] = 49 [8] = 64 [9] = 81 demo_index: vint[0] = 0 vint[1] = 1 vint[2] = 4 vint[3] = 9 vint[4] = 16 vint[5] = 25 vint[6] = 36 vint[7] = 49 vint[8] = 64 vint[9] = 81 demo_at: vint.at( 0 ) = 0 vint.at( 1 ) = 1 vint.at( 2 ) = 4 vint.at( 3 ) = 9 vint.at( 4 ) = 16 vint.at( 5 ) = 25 vint.at( 6 ) = 36 vint.at( 7 ) = 49 vint.at( 8 ) = 64 vint.at( 9 ) = 81 demo_iterator: *i = 0 *i = 1 *i = 4 *i = 9 *i = 16 *i = 25 *i = 36 *i = 49 *i = 64 *i = 81 demo_foreach_fkt: i = 0 i = 1 i = 4 i = 9 i = 16 i = 25 i = 36 i = 49 i = 64 i = 81 demo_foreach_fo aktuellesObjekt = 0 aktuellesObjekt = 1 aktuellesObjekt = 4 aktuellesObjekt = 9 aktuellesObjekt = 16 aktuellesObjekt = 25 aktuellesObjekt = 36 aktuellesObjekt = 49 aktuellesObjekt = 64 aktuellesObjekt = 81 Es wurden 10 Elemente ausgegeben.
AnyWare@Wachtler.de