René Nyffenegger's collection of things on the web | |
René Nyffenegger on Oracle - Most wanted - Feedback
- Follow @renenyffenegger
|
STL Beispiele und Code Fragmente | ||
Hier soll ein ganz kleiner Ueberblick gegeben werden, was mit der STL möglich ist. Keinesfalls kann dies
vollständig sein. Für weitere Details empfehle ich das vorzügliche Buch von Nicoloai M. Josuttis: The C++
Standard Library (A Tutorial and Reference). Es ist im Addison Wesley Verlag erschienen.
Ausschalten der Warnung 4768
Wenn die STL unter Visual C 6 verwendet wird, empfiehlt es sich, die Warning 4786 auszuschalten. Das wird mit dem folgenden pragma bewerkstelligt.
#pragma warning (disable: 4786) Header Dateien#include <iostream> #include <vector> #include <map> #include <string> #include <algorithm> #include <sstream> #include <iomanip> Namespace std
Die STL ist im Namespace std und ich möchte auf diesen Namespace defaultmässig zugreifen:
using namespace std; C Arrays
C Arrays werden in C++ (meist zurecht) als unsicher verschmäht und die STL gibt eine robuste Alternative dazu. Siehe
dazu aber auch das Beispiel weiter unten. Hier soll erst mal gezeigt werden, wie in C ein Array gefüllt
und ausgegeben wurde.
void c_arrays () { int a[40]; int* end=&a[40]; for (int i=0; i<= 40; i++) { a[i]= ((i% 5) * 7 ) % 13 + ((i%13) * 7 ) + 2; } for (int* ptr=a; ptr <= end; ptr++) { cout << *ptr << ","; } cout << endl << endl; } Achtung, mit Vectoren ist auch nicht alles erlaubt!
Bei einem Vektor kann (entsprechend C) nicht wahlfrei eingefügt werden. Dies darf meines Erachtens als
Nachteil aufgefasst werden. Ein Stück Code wie nachfolgend muss also vermieden werden.
void vector_no_no() { vector<int> v; v[1]=4; // stuerzt ab v[2]=3; // stuerzt ab } void vector_push_back_and_iterator() { cout << "vector push back and iterator"<<endl; vector<int> v; v.push_back(1); v.push_back(2); v.push_back(3); v.push_back(4); v.push_back(5); for ( vector<int>::iterator i=v.begin(); i<v.end(); i++ ) { cout << *i << ","; } cout << endl << endl; } template <class T> void printContainer(T& t) { for ( T::iterator i=t.begin(); i<t.end(); i++ ) { cout << *i << ","; } } Maps
Maps stellen eine Verbindung eines Datentypes zu einem anderen her.
void map_basic() { map <int,string> m; m[1]="eins"; m[2]="zwei"; m[3]="drei"; m[4]="vier"; m[5]="fuenf"; cout << m[3] << endl << endl; } Suchen in einem Vectorvoid vector_find() { cout << "vector find"<<endl; vector<int> v; v.push_back(1); v.push_back(2); v.push_back(3); v.push_back(4); v.push_back(5); vector<int>::iterator i = find(v.begin(), v.end(), 3); cout << "3 found at position" << i-v.begin() << endl; cout << endl << endl; } void vector_find_insert() { cout << "vector find insert" << endl; vector<int> v; v.push_back(1); v.push_back(2); v.push_back(3); v.push_back(4); v.push_back(5); vector<int>::iterator i = find(v.begin(), v.end(), 3); i = v.insert(i,50); v.insert(i,51); printContainer(v); cout << endl << endl; } void vector_copy_1() { cout << "vector copy 1"<<endl; vector<int> v; vector<int> w; insert_iterator<vector<int> > ii(w,w.begin()); v.push_back(1); v.push_back(2); v.push_back(3); v.push_back(4); v.push_back(5); w.push_back(50); w.push_back(49); w.push_back(48); w.push_back(47); w.push_back(46); w.push_back(45); w.push_back(44); w.push_back(43); w.push_back(42); w.push_back(41); copy(v.begin(), v.end(), w.begin()+2); printContainer(w); cout << endl << endl; } void vector_copy_2() { cout << "vector copy 2"<<endl; vector<int> v; vector<int> w; insert_iterator<vector<int> > ii(w,w.begin()); v.push_back(1); v.push_back(2); v.push_back(3); v.push_back(4); v.push_back(5); copy(v.begin(), v.end(), ii); printContainer(w); cout << endl << endl; } void vector_to_cout() { cout << "vector to cout"<<endl; vector<int> v; v.push_back(1); v.push_back(2); v.push_back(3); v.push_back(4); v.push_back(5); ostream_iterator<int> out(cout, " "); copy (v.begin(), v.end(), out); cout << endl << endl; } bool three_is_it(int i) { return i == 3; } void vector_find_if_1() { cout << "vector find if 1"<<endl; vector<int> v; v.push_back(1); v.push_back(2); v.push_back(3); v.push_back(4); v.push_back(5); v.push_back(3); v.push_back(9); // finde die (erste) drei und aendere sie in 99 *find_if(v.begin(),v.end(),three_is_it) = 99; printContainer(v); cout << endl << endl; } Sortieren eines Vektorsvoid vector_sort() { cout << "vector sort"<<endl; vector<int> v; v.push_back(6); v.push_back(3); v.push_back(1); v.push_back(3); v.push_back(5); v.push_back(9); v.push_back(4); v.push_back(2); v.push_back(8); // less waere default. // statt less waere auch greater moeglich sort(v.begin(),v.end(),less<int>()); printContainer(v); cout << endl << endl; } void vector_find_if_2() { cout << "vector find if 2"<<endl; vector<int> v; v.push_back(1); v.push_back(2); v.push_back(3); v.push_back(4); v.push_back(5); v.push_back(3); v.push_back(9); int z=4; // finde die (erste) zahl groesser als z und aendere sie in 99 *find_if(v.begin(),v.end(),bind2nd(greater<int>(),z)) = 99; printContainer(v); cout << endl << endl; } Einsatz von for_each
for_each ist sehr hilfreich: sie erlaubt es, eine Funktion fuer jedes Element eines Containers aufzurufen.
void func_print_elem_add_1(int elem) { cout << elem + 1 << endl; } void vector_for_each_func() { cout << "vector for each fund" << endl; vector<int> v; v.push_back(1); v.push_back(2); v.push_back(3); v.push_back(4); v.push_back(5); v.push_back(3); v.push_back(9); for_each(v.begin(), v.end(),func_print_elem_add_1); cout << endl << endl; }
Der Nachteil an obigem Beispiel ist jedoch, dass das "+ 1" fix verdrahtet
ist und man eventuell flexibler sein will (zB einmal 5 und ein andermal 7 addieren will.
Dies ist mit dem folgenden Ansatz moeglich:
Bedingung: die Klasse muss einen operator() aufweisen,
der als Argument den Typ des Containers nimmt.
for_each verbessertclass print_elem_add_n { int n_; public: print_elem_add_n(int n) : n_(n) {}; void operator() (int elem) {cout << elem + n_ << endl;}; }; void vector_for_each_class() { cout << "vector for each class" << endl; vector<int> v; v.push_back(1); v.push_back(2); v.push_back(3); v.push_back(4); v.push_back(5); v.push_back(3); v.push_back(9); for_each(v.begin(), v.end(),print_elem_add_n(44)); cout << endl << endl; }
Gleichwohl kann for_each auch dazu verwendet werden, die Elemente eines Containers zu verändern (Kraft der Referenz).
class='code'>class elem_add_n { int n_; public: elem_add_n(int n) : n_(n) {}; void operator() (int& elem) {elem += n_;}; }; void vector_for_each_modify() { cout << "vector for each fund" << endl; vector<int> v; v.push_back(1); v.push_back(2); v.push_back(3); v.push_back(4); v.push_back(5); v.push_back(3); v.push_back(9); for_each(v.begin(), v.end(),elem_add_n(42)); printContainer(v); cout << endl << endl; } stringstreams
Stringstreams können dazu verwendet werden, formatierte Ausgaben eines Strings zu machen.
void stringstreams() { cout << "stringstream" << endl; stringstream s; s << "alle " << 9 << 'e'; cout << s.str() << endl; // Den String Stream wieder zuruecksetzen. s.str(""); s << setw(20) << "zwanzig" << setfill('*') << setw(30) << "Stern"; cout << s.str() << endl; cout << endl<<endl; } StringsAuftrennen von Strings
Das folgende Stück Code trennt einen Namen (bestehend aus Vornamen und Nachnamen durch Blank getrennt) in
Vornamen und Nachnamen auf. STL Strings definieren einen Datentyp size_type, welcher immer für
Positionen innerhalb eines Strings verwendet werden sollte. Falls kein Leerzeichen gefunden wird, liefert
string.find npos zurück.
void split_name( const string& fullName, string& firstName, string& lastName) { string::size_type posOfSpace = fullName.find(' '); if (posOfSpace == string::npos) throw "String enthaelt kein Leerzeichen"; firstName = fullName.substr(0,posOfSpace); lastName = fullName.substr(posOfSpace+1); } Binäre Daten im String
Ein STL String kann, im Gegensatz zu C Strings Nulls enthalten.
string s; s.assign("abc\0def\0ghi\0",12); cout << "Laenge: " << s.length() << endl; DateienErstellen und schreiben einer Dateivoid create_or_replace_file(const string& file_name) { ofstream a_file(file_name.c_str()); a_file << "Erste Zeile" << endl; a_file << "Zweite Zeile" << endl; } Anfügen an eine Dateivoid append_or_replace_file(const string& file_name) { ofstream a_file(file_name.c_str(), ios::out | ios::app); a_file << "Erste Zeile" << endl; a_file << "Zweite Zeile" << endl; } Konvertieren einer Zeichenfolge in einen int#include <sstream> void ConvertStringToInt() { stringstream s; s << "5340 4090"; int i; int j; s >> i; s >> j; cout << "i: " << i << endl << "j: " << j << endl; } Zeichenweises Einlesen aus einer Datei
Beim Einlesen aus einer Datei kann mittels eof() festgestellt werden, ob
das Ende der Datei erreicht worden ist. ACHTUNG, eof wird erst gesetzt, wenn versucht
wurde, ein Zeichen zu lesen, nachdem das letzte Zeichen erreicht wurde. Nach dem
Lesen des letzten Zeichens gibt eof() noch false zurück.
void read_char_for_char_from_file(const string& file_name) { ifstream a_file(file_name.c_str()); for (;;) { char c; a_file.get(c); // EOF wird erst gesetzt, wenn versucht wurde // _hinter_ das Ende der Datei zu lesen! if (a_file.eof()) break; cout << c; } } Zeilenweises Einlesen aus einer Datei
Version mit char[]
#include <fstream> #include <iostream> #include <sstream> using namespace std; int main(int argc, char* argv[]) { int buf_len = 80; if (argc<2) { cout << "No buf size stated, defaulting to 80" << endl << endl; cin} else { // Siehe Konvertieren einer Zeichenfolge in einen int stringstream s; s << argv[1]; s >> buf_len; } char* buf = new char[buf_len]; ifstream a_file("test_datei"); while (a_file.getline(buf, buf_len)) { cout << buf << endl; } delete [] buf; return 0; }
Version mit string
#include <fstream> #include <iostream> #include <string> using namespace std; int main() { string line; ifstream a_file("test_datei"); while (getline(a_file, line)) { cout << line << endl; } } Lesen der Standardeingabe#include <iostream> #include <string> using namespace std; int main() { string line; while (getline(cin, line)) { cout << line << endl; } return 0; } Readsome
Das folgende ist ein Versuch, und was ich erreichen wollte, klappte nicht. Also mit Vorsicht geniessen.
#include <fstream> #include <sstream> #include <iostream> using namespace std; int main(int argc, char* argv[]) { int buf_len = 80; if (argc<2) { cout << "No buf size stated, defaulting to 80" << endl << endl; } else { // Siehe Konvertieren einer Zeichenfolge in einen int stringstream s; s << argv[1]; s >> buf_len; } char* buf = new char[buf_len]; ifstream a_file("test_datei"); cout << a_file.gcount() << endl; do { a_file.read(buf, buf_len-1); buf[buf_len]=0; cout << buf << "---"; } while (a_file); delete [] buf; return 0; } Das Hauptprogrammint main(int argc, char* argv[]) { c_arrays(); // vector_no_no(); vector_push_back_and_iterator(); map_basic(); vector_find(); vector_copy_1(); vector_copy_2(); vector_to_cout(); vector_find_insert(); vector_find_if_1(); vector_sort(); vector_find_if_2(); vector_for_each_func(); vector_for_each_class(); vector_for_each_modify(); stringstreams(); string firstName; string lastName; split_name("Hans Meier",firstName, lastName); cout << "Vorname: " << firstName << ", Nachname: " << lastName << endl; // Dateien create_or_replace_file("c:\temp\bla.txt"); append_or_replace_file("c:\temp\foo.txt"); read_char_for_char_from_file("c:\temp\bla.txt"); ConvertStringToInt(); return 0; }
Hier hat's ein Beispiel eines Manipulators, der eine Windows Message Box erzeugt.
|