Einführung in die Nutzung der
Standard Template Library (STL)








Literatur:
(Amme) - Ammeraal, L.: STL for C++-Programmers;
Chichester: Wiley, 1997
(Brey) - Breymann, U.: C++ Einfüfung und professionelle Programmierung;
München: Hanser, 2007 (9. Auflage)

Überblick

Konzepte der C++-STL

Das grundlegende Konzept

Container

Iteratoren

Algorithmen

Beispiel

Lesen und Ausgeben einer variablen Anzahl von Integer-Zahlen ungleich 0, die Werte sollen in einer geeigneten Datenstruktur gespeichert werden.

Realisierung ohne STL

#include <iostream.h>

int main()
{
  int vektor[1000]; // Maximal 1000 Elemente !!
  int i = 0, j;
  int x;
  cout << "Enter positive integers, followed by 0:\n";
  while (cin >> x, x != 0)
    {
      vektor[i] = x;
      i++;
    }
  cout << endl;  
  for ( j = 0; j < i; j++)
    cout << vektor[j] << " ";
  cout << endl;
  return 0;
}

Realisierung mit STL

// readwr.c: Reading and writing a variable number of
//   nonzero integers (followed in the input by 0).
// From: Ammeraal, L. (1997) STL for C++ Programmers,
//       Chichester: John Wiley.

#include <iostream> 
//fuer g++ erst ab Version 2.8 verfuegbar
#include <vector.h>

int main()
{  vector<int> v;
   int x;
   cout << "Enter positive integers, followed by 0:\n";
   while (cin >> x, x != 0)
      v.push_back(x);
   vector<int>::iterator i;
   for (i=v.begin(); i != v.end(); ++i) 
      cout << *i << " ";
   cout << endl;
   return 0;
}

Sequentielle Container

Das einführende Beispiel verwendete das Schlüsselwort vector. In diesem Programm kann vector vollständig durch list bzw. dequeue (Abk. für double ended queue) ersetzt werden. Es folgt das Listing für dequeue:
// readwrd.c: Reading and writing a variable number of
//   nonzero integers (followed in the input by 0).
// From: Ammeraal, L. (1997) STL for C++ Programmers,
//       Chichester: John Wiley.
#include <iostream.h>
#include <deque>

int main()
{  deque<int> v;
   int x;
   cout << "Enter positive integers, followed by 0:\n";
   while (cin >> x, x != 0)
      v.push_back(x);
   deque<int>::iterator i;
   for (i=v.begin(); i != v.end(); ++i) 
      cout << *i << " ";
   cout << endl;
   return 0;
}
Der Nutzer bemerkt keinen Unterschied zwischen den drei möglich Versionen, Unterschiede bestehen aber in der internen Darstellung und in den möglichen Operationen:
Operation Funktion vector list deque
Einfügen am Ende push_back j j j
Löschen am Ende pop_back j j j
Einfügen am Anfang push_front n j j
Löschen am Anfang pop_front n j j
Einfügen irgendwo insert (j) j (j)
Löschen irgendwo erase (j) j (j)
Sortieren sort j n j

Im Folgenden soll ein Beispiel für Einfüge- und Löschoperationen in Listen angegeben werden:

// insdel.cpp: Insertions and deletions in a list.
// From: Ammeraal, L. (1997) STL for C++ Programmers,
//       Chichester: John Wiley.
#include <iostream>
#include <list>          

void showlist(const char *str, const list<int> &L)
{  list<int>::const_iterator i;
   cout << str << endl << "  ";
   for (i=L.begin(); i != L.end(); ++i)
      cout << *i << " ";
   cout << endl;
}          

int main()
{  list<int> L;                
   int x;
   cout << "Enter positive integers, followed by 0:\n";
   while (cin >> x, x != 0)
      L.push_back(x);
   showlist("Initial list:", L);
   L.push_front(123);
   showlist("After inserting 123 at the beginning:", L);
   list<int>::iterator i = L.begin();
   L.insert(++i, 456);
   showlist(
      "After inserting 456 at the second position:", L);
   i = L.end(); 
   L.insert(--i, 999);
   showlist(
      "After inserting 999 just before the end:", L);
   i = L.begin(); x = *i;
   L.pop_front(); 
   cout << "Deleted at the beginning: " << x << endl;
   showlist("After this deletion:", L);
   i = L.end(); x = *--i;
   L.pop_back();
   cout << "Deleted at the end: " << x << endl;
   showlist("After this deletion:", L);
   i = L.begin();
   x = *++i; cout << "To be deleted: " << x << endl;
   L.erase(i);
   showlist("After this deletion (of second element):", 
      L);
   return 0;
}

Sortieren

Das folgende Beispiel ist eine Erweiterung des Programms readwr.c. Das Programm sortiert den Vektor v in aufsteigender Reihenfolge.
// sort1.cpp: Sorting a vector.
// From: Ammeraal, L. (1997) STL for C++ Programmers,
//       Chichester: John Wiley.
#include <iostream>
#include <vector>
#include <algorithm>

int main()
{  vector<int> v;
   int x;
   cout << "Enter positive integers, followed by 0:\n";
   while (cin >> x, x != 0) v.push_back(x);
   vector<int>::iterator i;
   cout << "Before sorting :\n";
   for (i=v.begin(); i != v.end(); ++i)
      cout << *i << " ";
   cout << endl;
   sort(v.begin(), v.end());
   cout << "After sorting: \n";
   for (i=v.begin(); i != v.end(); ++i) 
      cout << *i << " ";
   cout << endl;
   return 0;
}
Die Sortierung wird durch den Befehl:
sort(v.begin(), v.end());
ausgeführt.

Weitere Algorithmen

Die folgend angegebenen Algorithmen werden nur ansatzweise gezeigt:

find

....
   cin >> x;
   vector<int>::iterator i = 
      find(v.begin(), v.end(), x);
   if (i == v.end()) 
      cout << "Not found\n";
   else
   {  cout << "Found"; 
....

copy und ein Insert Iterator

....
{  int a[4] = {10, 20, 30, 40};
   vector<int> v(a, a+4);
   list<int> L(4);  // A list of 4 elements
   copy(v.begin(), v.end(), L.begin()); // copying 4 elements to L
   list<int>::iterator i;
   for (i=L.begin(); i != L.end(); ++i)
      cout << *i << " "; // Output: 10 20 30 40
.....
Problem: Im obigen Beispiel werden Elemente der Zielliste L überschrieben, was manchmal (häufig) nicht gewünscht ist. Lösung: Nutzung eines inserter
....
{  int a[4] = {10, 20, 30, 40};
   vector<int> v(a, a+4);
   list<int> L(5, 123);  // A list of 5 elements, all are 123
   list<int>::iterator i = L.begin();
   ++i; ++i;
   copy(v.begin(), v.end(), inserter(L, i));
....

merge - Mischen

....
  vector<int> a(5);
   a[0] = 2; a[1] = 3; a[2] = 8; 
   a[3] = 20; a[4] = 25;
   int b[6] = {7, 9, 23, 28, 30, 33};
   list<int> c; // List c is initially empty
   merge(a.begin(), a.end(), b, b+6, 
      inserter(c, c.begin()));
....

Strings

Das Fehlen spezieller string-Funktionen in C/C++ wurde lange Zeit kritisiert. Was? Man muß eine Funktion rufen, um 2 Strings aneinander zu ketten? war ein lange Zeit üblicher Kommentar. Das C++-String-Template bietet einen großen Teil der Funktionen, die Containerklassen wie vector enthalten. Desweiteren wird eine Menge von Suchfunktionen angeboten, die Teilzeichenketten suchen.
Beispiel:
// Phil Ottewell's STL Course - http://www.yrl.co.uk/~phil/stl/stl.htmlx
//
// Example 5.1                  � Phil Ottewell 1997 <phil@yrl.co.uk>
//
// Purpose:
//          Demonstrate use of standard string class

// ANSI C Headers
#include <stdlib.h>

// C++ STL Headers
#include <algorithm>
#include <iostream>
#include <string>

#ifdef _WIN32
using namespace std;
#endif

int main( int argc, char *argv[] )
{
    size_t ip;
    string needle = "needle";     // Initialize with C style string literal
    string line("my string");     // Ditto
    string haystack(line,0,3);    // Initialize with another string starting
                                  // at element 3, i.e. "string"
    string string3(line,0,2);     // Initialize with first 2 characters of
                                  // line, i.e. "my"
    string s1;                    // INITIALIZING with single character, e.g.
    string s2;                    // = 'A' or ('A') or an integer NOT ALLOWED
                                  // These will currently have .length() of zero
    string dashes(80,'-');        // You can initialize using a character like
                                  // this, and character ASSIGNMENT is legal

    char old_c_string[64];

//  Concatenation using + operator
    s1 = "Now is the Winter ";
    s2 = "of our discontent made Summer";
    cout << "s1 = \"" << s1 << "\"," << "s2 = \"" << s2 << "\"\n"
         << "s1 + s2 = \"" << s1 + s2 << "\"" << endl << dashes << endl;

//  Find a substring in a string
    haystack = "Where is that " + needle + ", eh ?";
    cout << "haystack = \"" << haystack << "\"" << endl;
    ip = haystack.find(needle);
//  Use substr function to get substring - use string::npos (the "too large"
//  character count) to get the rest of the string
    cout << "ip = haystack.find(needle) found \""
         << haystack.substr(ip,string::npos )
         << "\" at position ip = " << ip << endl << dashes << endl;

//  Demonstrate use of Algorithms with strings
    line = "Naomi, sex at noon taxes, I moan";
    cout << line << " [Algorithm: reverse(line.begin(),line.end())]" << endl;
    reverse( line.begin(), line.end() );
    cout << line << " [line.length() = " << line.length() << "]" << endl
         << dashes << endl;

//  Passing a string to a function requiring a C style string
    line = "copyright";
    strncpy( old_c_string,   line.c_str()   , sizeof(old_c_string)-1 );

    old_c_string[sizeof(old_c_string)-1] = '\0';
    cout << "strncpy \"" << line << "\" to c string which now contains \""
         << old_c_string << "\"" << endl << dashes << endl;

//  Insert into a string
    s1 = "piggy middle";
    s2 = "in the ";
    cout << "s1 = \"" << s1 << "\", s2 = \"" << s2 << "\"" << endl;
    s1.insert(6,s2); // Insert s2 in s1
    cout << "s1.insert(6,s2) = " << s1 << endl << dashes << endl;

//  Erase
    cout << "[Use s1.erase(ip,4) to get rid of \"the \"]" << endl;
    ip = s1.find("the");
    if ( ip != string::npos ) s1.erase( ip, 4 ); // Note check on ::npos
    cout << s1 << endl << dashes << endl;

//  Replace 
    cout << "[Use s1.replace(ip,2,\"is not in the\") to replace "
         << "\"in\" with \"is not in the\"]" << endl;
    ip = s1.find("in");
//  Note inequality check on string::npos to see if search string was found
    if ( ip != string::npos ) s1.replace( ip, 2, "is not in the" );
    cout << s1 << endl << dashes << endl;

    return( EXIT_SUCCESS );
}

Iteratoren

Iteratoreigenschaften

Zustände

Iteratoren sind eine Verallgemeinerung von Zeigern. Sie erlauben es, mit verschiedenen Containern auf gleichartige Weise zu arbeiten. Ein Iterator kann verschiedene Zustaende haben.

Kategorien

Assoziative Container

Map und Multimap

#include <map>
Some Map Access Functions Purpose
begin() Returns iterator pointing to
  first element
end() Returns iterator pointing _after_
  last element
swap( , ) Swap two elements
insert( , ) Insert a new element
size() Number of elements in map
max_size() Maximum possible number of elements
  in map
empty() True if map is empty
[ ] Subscript search access operator
// Beispiel fuer Abbildung (map)
#include<map>
#include<string>

// zur Abkuerzung zwei typedefs
// Vergleichsobjekt:  less<long>()
typedef map<long, string> Abbildungstyp;
typedef Abbildungstyp::value_type Wertepaar;


int main()
{
    Abbildungstyp Abbildung;

    Abbildung.insert( Wertepaar(836361136, "Andreas"));
    Abbildung.insert( Wertepaar(274635328, "Bernd"));
    Abbildung.insert( Wertepaar(260736622, "Juergen"));
    Abbildung.insert( Wertepaar(720002287, "Karin"));
    Abbildung.insert( Wertepaar(138373498, "Thomas"));
    Abbildung.insert( Wertepaar(135353630, "Uwe"));
    // Einfuegen von Xaver wird nicht ausgef�hrt, weil
    //   der   Schluessel schon vorhanden ist.
    Abbildung.insert( Wertepaar(720002287, "Xaver"));

/* 

Die Ausgabe der Namen ist wegen der unterliegenden 
Implementierung nach Nummern sortiert: 

*/ 

cout << "Ausgabe:\n";
    Abbildungstyp::iterator iter = Abbildung.begin();
    while(iter != Abbildung.end())
    {
          cout << (*iter).first << ':'     // Nummer
               << (*iter).second           // Name
               << endl;
          ++iter;
    }


    cout << "Ausgabe des Namens nach Eingabe der Nummer\n"
         << "Nummer: ";
    long Nummer;
    cin >> Nummer;
    iter = Abbildung.find(Nummer);    // O(log N), siehe Text


    if(iter != Abbildung.end())
        cout << (*iter).second << ' ' // O(1)
             << Abbildung[Nummer]     // O(log N)
             << endl;
    else cout << "Nicht gefunden!" << endl;
}

Set und Multiset

#include <set>
Set speichert nur einfache Schlüssel, das heißt der Schlüssel ist der Wert.
Some Set Access Functions Purpose
begin() Returns iterator pointing to first
  element
end() Returns iterator pointing _after_
  last element
swap( , ) Swap two elements
insert( , ) Insert a new element
size() Number of elements in set
max_size() Maximum possible number of
  elements in set
empty() True if set is empty
// Beispiel fuer Menge (set)
#include<set>
#include<showseq>

int main()
{
    int i;
    set<int> Menge;  // Vergleichsobjekt:  less<int>()

    for(i = 0; i < 10; i++) Menge.insert(i);
    for(i = 0; i < 10; i++) Menge.insert(i); // ohne Wirkung
    showSequence(Menge);                     // 0 1 2 3 4 5 6 7 8 9

/*
Die Anzeige demonstriert, da� die Elemente der Menge wirklich genau
einmal vorkommen. Im folgenden werden Elemente geloescht, wobei in 
der ersten Variante zunaechst das Element gesucht wird, um
es dann mit dem gefundenen Iterator zu loeschen. In der zweiten 
Variante wird die Loeschung ueber den angegebenen Schl�ssel 
vorgenommen. 
*/ 

cout << "Loeschen per Iterator\n"
            "Welches Element loeschen? (0..9)" ;
    cin >> i;
    set<int>::iterator iter = Menge.find(i);
    if(iter == Menge.end())
       cout << i << " nicht gefunden!\n";
    else
    {
        cout << "Es gibt " << Menge.count(i)            // 1
             << "-mal das Element " << i << endl;
        Menge.erase(iter);
        cout << i << " geloescht!\n";
        cout << "Es gibt " << Menge.count(i)            // 0
             << "-mal das Element " << i << endl;
    }
    showSequence(Menge);
}

Algorithmen

Es existieren 60 Algorithmen, die in 8 Klassen eigeteilt werden können: Detaillierte Beschreibung - siehe (Brey)
Dr.Andreas Mueller 2003-10-20