René Nyffenegger's collection of things on the web
René Nyffenegger on Oracle - Most wanted - Feedback -
 

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 Vector

void 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 Vektors

void 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 verbessert

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

Strings

Auftrennen 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;

Dateien

Erstellen und schreiben einer Datei

void 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 Datei

void 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 Hauptprogramm

int 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.