C++ Kurs

Algorithmen I

Die Themen:

Die Standard-Bibliothek stellt eine große Anzahl von Algorithmen zur Verfügung um Elemente in einem Container zu verarbeiten. Einige Algorithmen wurden in den vorherigen Kapiteln ja schon erwähnt, wie z.B. for_each(...).

Die Algorithmen lassen sich grundsätzlich in folgende Kategorien einteilen:

Dieses und das nachfolgende Kapitel enthält eine Übersicht über die Algorithmen. Die Algorithmen werden nur kurz beschrieben, eine ausführliche Referenz finden Sie in der Online-Dokumentation zur Standard-Bibliothek. Jedoch sollten Sie nach dem Durchlesen dieses Kapitels und den aufgeführten Beispielen in der Lage sein, den für Ihr Problem richtigen Algorithmus zu finden und unter Zuhilfenahme der Online-Dokumentation diesen auch einzusetzen.

Bis auf die numerischen Algorithmen sind alle Algorithmen in der Header-Datei algorithm definiert. Die Definitionen der numerischen Algorithmen sind in der Header-Datei numeric enthalten.

Vorbereitungen

Bevor wir mit der Behandlung der Algorithmen beginnen, müssen wir noch etwas Vorarbeit leisten. Die Demonstration der Algorithmen soll soweit wie möglich praxisnah erfolgen, d.h. wir werden die Algorithmen nicht nur auf einfache Datentypen anwenden, sondern auch auf Objekte. Dazu definieren wir uns folgende Klasse Object:

PgmHeader#ifndef OBJECT_H__
#define OBJECT_H__

#include <iostream>
#include <string>

// Makro für Debug-Ausgabe
#ifdef TRACE_METH
    #define TRACE(_T)   std::cout << _T << " Obj-Nr:" << num << std::endl
#else
    #define TRACE(_T)
#endif

// Object Klasse
class Object
{
   std::string text;  // Objektdatum
   static int count;  // Objektzähler
   int num;           // aktuelle Objektnummer
public:
   Object();                   // Standard-ctor
   Object(const char* pText);  // ctor
   Object(const Object& obj);  // copy-ctor
   Object(Object&& obj);       // move-ctor
   virtual ~Object();          // dtor
   // Zuweisungsoperator
   Object& operator = (const Object& rhs);
   // Move-Operator =
   Object& operator = (Object&& rhs);
   // Vergleichsoperatoren
   bool operator < (const Object& rhs) const;
   bool operator == (const Object& rhs) const;
   // Restl. Vergleichsops auf Basis von < und == definieren
   bool operator != (const Object& rhs) const
   {
        return !(*this == rhs);
    }
    bool operator > (const Object& rhs) const
    {
        return rhs < *this;
    }
    bool operator <= (const Object& rhs) const
    {
        return !(rhs < *this);
    }
   bool operator >= (const Object& rhs) const
   {
        return !(*this < rhs);
    }
   // Ausgabeoperator
   friend std::ostream& operator << (std::ostream& os, const Object& obj);
};
// Statisches Datum definieren
decltype(Object::count) Object::count=0;
// ctor-Definitionen
inline Object::Object()
{
   num = ++count;
   TRACE("std-ctor");
}
inline Object::Object(const char* pText)
{
   num = ++count;
   text = pText;
   TRACE("ctor");
}
inline Object::Object(const Object& obj)
{
   num = ++count;
   text = obj.text;
   TRACE("copy-ctor");
}
inline Object::Object(Object&& obj)
{
   text = std::move(obj.text);
   num = obj.num;
   obj.text.clear();
   obj.num = 0;
   TRACE("move-ctor");
}
// dtor Definition
inline Object::~Object()
{
   TRACE("dtor");
}
// Überladene Operatoren definieren
inline Object& Object::operator = (const Object& rhs)
{
   if (&rhs==this)
      return *this;
   text = rhs.text;
   TRACE("operator=");
   return *this;
}
inline Object& Object::operator = (Object&& rhs)
{
   text = std::move(rhs.text);
   num = rhs.num;
   rhs.text.clear();
   rhs.num = 0;
   TRACE("move operator=");
   return *this;
}
inline bool Object::operator < (const Object& rhs) const
{
   return text < rhs.text;
}
inline bool Object::operator == (const Object& rhs) const
{
   return text == rhs.text;
}
inline std::ostream& operator << (std::ostream& os, const Object& obj)
{
   std::cout << obj.text;
   return os;
}
#endif

Diese Klasse Object enthält alle für die Ablage von Objekten in Containern notwendigen Memberfunktionen, wie z.B. den Kopierkonstruktor oder den überladenen Zuweisungsoperator. Ferner besitzt die Klasse die entsprechenden Vergleichsoperatoren, damit Objekte der Klasse in Containern sortiert und gesucht werden können. Desweiteren wurden die Move-Memberfunktionen definiert, um einen optimalen Programmablauf zu erhalten.

Die Memberfunktionen wurden ebenfalls in der Header-Datei object.h definiert. Dies entspricht zwar nicht den allgemein üblichen Programmier-Regeln, sondern dient lediglich zur Vereinfachung des Compileraufrufs. In der Regel wird in der Header-Datei ohject.h lediglich die Klasse definiert und in einer weiteren Datei object.cpp die Memberfunktionen der Klasse.

Zusätzlich wurde noch die Möglichkeit eingebaut, die Erstellung und das Löschen der Objekte protokollieren zu können. Um die Protokollierung zu aktivieren, ist das Symbol TRACE_METH vor dem Einbinden der Header-Datei object.h zu definieren.

So, sehen wir uns jetzt die erste Algorithmen-Kategorie an, die nicht-modifizierenden Algorithmen

Nicht-modifizierende Algorithmen

Folgende nicht-modifizierende Algorithmen stehen zur Verfügung:

Algorithmus Beschreibung
adjacent_find() Sucht nach benachbarten Elementen, deren 'Werte' gleich sind
count() Zählt die Anzahl der Elemente, die einen bestimmten 'Wert' besitzen
count_if() Zählt die Anzahl der Elemente, die einer bestimmten Bedingung genügen
equal() Vergleicht zwei Containerbereiche auf Gleichheit
find() Sucht nach Element mit einem bestimmten 'Wert'
find_if() Sucht nach Element das einer bestimmten Bedingung genügt
find_end() Sucht nach dem letzten Auftreten einer Sequenz in einem Bereich
find_first_of() Sucht nach dem ersten Auftreten eines von mehreren Elementen
for_each() Führt für jedes Element eine Operation durch
lexicographical_compare() Vergleicht zwei Bereiche auf lexikographisch kleiner
max_element() Liefert das größte Element
min_element() Liefert das kleinste Element
mismatch() Liefert die erste Position zurück an der zwei Container verschieden sind
search() Sucht nach einem überstimmenden Bereich
search_n() Sucht nach dem ersten n-fachen Auftreten eines Elements
HinweisDie hier aufgeführten Algorithmen arbeiten mit den Werten/Eigenschaften der Container-Elemente und nicht mit den Elementen, d.h. zwei unterschiedliche Objekte können durchaus gleich sein, wenn deren Eigenschaften den gleichen Inhalt besitzen. Gleichheit bedeutet in diesem Zusammenhang, dass der Operator == für die zu vergleichenden Elemente true zurückliefert.

Beispiel zu Suchen und Finden

PgmHeader// Beispiel zu Algorithmen
// Suchen und Finden


#include <iostream>
// Algorithmen einbinden
#include <algorithm>
// ostream_iterator einbinden
#include <iterator>
// vordefinierte Functors einbinden
#include <functional>
// Listen-Container einbinden
#include <list>

using std::cout;
using std::endl;

// #define TRACE_METH
#include "object.h"

int main()
{
    // Liste definieren und fuellen
    using cType = std::list<Object>;
    cType cont {"Eins","Zwei","Zwei","Drei","Zwanzig"};

    // Liste ausgeben
    cout << "Container-Inhalt: ";
    std::copy(cont.begin(), cont.end(), std::ostream_iterator<Object>(cout, ", "));
    cout << '\n';

    // Suche nach einem bestimmten Element mit find()
    auto iter = std::find(cont.begin(), cont.end(), Object("Zwei"));
    if (iter != cont.end())
        cout << "'Zwei' an Position " << (unsigned long)(std::distance(cont.begin(),iter))
             << " gefunden\n";
    else
        cout << "'Zwei' nicht gefunden!\n";

    // Suche nach ersten Objekt dessen Wert größer 'F' ist mit find_if()
    // Vergleich erfolgt mit Hilfe einer Lambda-Funktion

    iter = std::find_if(cont.begin(), cont.end(),
                        [] (Object& obj)
                        {
                            return obj > Object("F");
                        });

    if (iter != cont.end())
        cout << "1. Eintrag > 'F' an Position "
             << (unsigned long)(std::distance(cont.begin(),iter))
             << " gefunden\n";
    else
        cout << "Kein Objekt mit Wert groesser 'F' gefunden!\n";

    // Suche nach erstem doppelten Eintrag mit adjacent_find()
    iter = std::adjacent_find(cont.begin(),cont.end());
    if (iter != cont.end())
            cout << "Benachbarte gleiche Werte auf Position "
                 << (unsigned long)(std::distance(cont.begin(),iter)) << " gefunden\n";
    else
            cout << "Keine benachbarten gleiche Werte gefunden!\n";
}
ProgrammausgabeContainer-Inhalt: Eins, Zwei, Zwei, Drei, Zwanzig,
'Zwei' an Position 1 gefunden
1. Eintrag > 'F' an Position 1 gefunden
Benachbarte gleiche Werte auf Position 1 gefunden

Beispiel zu Zählen und Minimum/Maximum

PgmHeader// Beispiel zu Algorithmen
// Zählen und Minimum/Maximum


#include <iostream>
// Algorithmen einbinden
#include <algorithm>
// ostream_iterator einbinden
#include <iterator>
// vordefinierte Functors einbinden
#include <functional>
// Vektor-Container einbinden
#include <vector>

using std::cout;
using std::endl;

// #define TRACE_METH
#include "object.h"

int main()
{
    // Vektor definieren
    using cType = std::vector<Object>;
    cType cont;

    // Für Optimierung, verhindert die erneute Speicherreservierung
    // bei jedem neu hinzugefügten Objekt

    cont.reserve(10);
    // Vektor vorbelegen, diesmal mit push_back()
    cont.push_back(Object("Eins"));
    cont.push_back(Object("Zwei"));
    cont.push_back(Object("Drei"));
    cont.push_back(Object("Zwanzig"));
    cont.push_back(Object("Zwei"));
    cout << "Container-Inhalt: ";
    std::copy(cont.begin(), cont.end(), std::ostream_iterator<Object>(cout,", "));
    cout << '\n';

    // Anzahl der Objekte mit dem Wert "Zwei" zählen
    auto result = std::count(cont.begin(), cont.end(),Object("Zwei"));
    cout << "Objekte mit dem Wert 'Zwei': " << (unsigned long)result << endl;

    // Anzahl der Objekte zählen die größer/gleich "Z" sind
    // Vergleichsobjekt definieren
    Object compObj("Z");
    // und in Lambda-Funktion einbinden per Referenz
    result = std::count_if(cont.begin(), cont.end(),
                          [&compObj](Object& obj)
                          {
                              return (obj > compObj);
                          });

    cout << "Objekte mit dem Wert >= 'Z': " << (unsigned long)result << endl;
   
    // Kleinstes und größtes Objekt finden
    auto iterMin = std::min_element(cont.begin(), cont.end());
    auto iterMax = std::max_element(cont.begin(), cont.end());
    cout << "Kleinstes Element: '" << *iterMin
         << "', groesstes Element: '" << *iterMax << "'\n";
}
ProgrammausgabeContainer-Inhalt: Eins, Zwei, Drei, Zwanzig, Zwei,
Objekte mit dem Wert 'Zwei': 2
Objekte mit dem Wert >= 'Z': 3
Kleinstes Element: 'Drei', groesstes Element: 'Zwei'

Beispiel zu Container-Vergleiche

PgmHeader// Beispiel zu Algorithmen
// Container-Vergleich


#include <iostream>
// Algorithmen einbinden
#include <algorithm>
// ostream_iterator einbinden
#include <iterator>
// vordefinierte Functors einbinden
#include <functional>
// Vektor-Container einbinden
#include <vector>

using std::cout;
using std::endl;

// #define TRACE_METH
#include "object.h"

int main()
{
    // Container definieren und initialisieren
    using cType = std::vector<Object>;
    cType cont {"Eins","Zwei","Zwei","Drei","Zwanzig"};

    cout << "Container-Inhalt: ";
    std::copy(cont.begin(), cont.end(), std::ostream_iterator<Object>(cout,", "));
    cout << '\n';

    // Zweiten Container definieren und mit dem Inhalt des
    // ersten Containers initialisieren

    cType toCmp(cont);
    // Viertes Element überschreiben
    // Die String-Zuweisung erstellt über den ctor Object(const char*)
    // ein neues Objekt und übernimmt dieses in den Container

    toCmp.at(3) = "Null";
    cout << "Vergleichs-Container: ";
    std::copy(toCmp.begin(), toCmp.end(), std::ostream_iterator<Object>(cout,", "));
    cout << '\n';

    // Vergleiche nun die ersten 3 Elemente der Container miteinander
    auto bRet = std::equal(cont.begin(),cont.begin()+3,toCmp.begin());
    if (bRet)
        cout << "Bereich [0..3) der Container identisch\n";
    else
        cout << "Bereich [0..3) der Container nicht identisch\n";

    // Suche nun die erste Position, an der die beiden Container
    // unterschiedliche Wert haben. Da der Vergleich auf unterschiedlichen
    // Positionen in den Containern beginnen kann, liefert mismatch()
    // zwei Iteratoren zurück, einen für die Position im ersten Container
    // und einen fuer die im zweiten Container
    // pair fuer die die beiden Positionen definieren

    auto iterMissed = std::mismatch(cont.begin(),cont.end(),toCmp.begin());
    if (iterMissed.first != cont.end())
        cout << "Container weichen ab Position "
             << (unsigned long)(std::distance(cont.begin(),iterMissed.first))
             << " im ersten Container voneinander ab\n";
    else
        cout << "Container haben identischen Inhalt";

}
ProgrammausgabeContainer-Inhalt: Eins, Zwei, Zwei, Drei, Zwanzig,
Vergleichs-Container: Eins, Zwei, Zwei, Null, Zwanzig,
Bereich [0..3) der Container identisch
Container weichen ab Position 3 im ersten Container voneinander ab

Modifizierende Algorithmen

Folgende modifizierende Algorithmen stehen zur Verfügung:

Algorithmus Beschreibung
copy() Kopiert einen Bereich
copy_backward() Kopiert einen Bereich, wobei das letzte Element des Bereichs als erstes  kopiert wird
for_each() Führt für jedes Element eine Operation durch
generate() Erzeugt für einen Bereich neue Elemente
generate_n() Erzeugt für n Elemente neue Elemente
merge() Vereinigt zwei Bereiche
replace() Ersetzt in einen Bereich bestimmte Werte der Elemente
replace_if() Ersetzt in einem Bereich die Werte der Elemente, die einer bestimmten Bedingung genügen
replace_copy() Ersetzt in einem Bereich bestimmte Werte der Elemente und kopiert dann den Bereich
replace_copy_if() Ersetzt in einem Bereich die Werte der Elemente die einer bestimmten Bedingung genügen und kopiert dann den Bereich
swap_ranges() Vertauscht zwei Bereiche
fill() Ersetzt in einem Bereich alle Elemente durch ein anderes Element
fill_n() Ersetzt n Elemente durch ein anderes Element
transform() Modifiziert die Werte eines Bereichs und kopiert diesen optional

Der Unterschied zwischen generate(), replace() und transform() besteht darin, dass generate() über eine definierbare Operation neue Elemente erzeugt, replace() bestehende Elemente ersetzt und transform() das Element selbst zwar nicht verändert, wohl aber seinen Wert.

Beispiel zu den modifizierenden Algorithmen

PgmHeader// Beispiel zu Algorithmen
// Modifizierende Algorithmen


#include <iostream>
// Algorithmen einbinden
#include <algorithm>
// ostream_iterator einbinden
#include <iterator>
// vordefinierte Functors einbinden
#include <functional>
// Container einbinden
#include <deque>
#include <vector>

using std::cout;
using std::endl;

// #define TRACE_METH
#include "object.h"

// Functor zur Erzeugung von Zufallszahlen
// mit einer Obergrenze

class Randomize
{
    int upperLimit;
  public:
    Randomize(int limit): upperLimit(limit)
    {}
    int operator ()()
    {
        return rand() % upperLimit;
    }
};

int main()
{
    // Container definieren/initialisieren
    using cType = std::vector<Object>;
    cType cont {"Eins","Zwei","Drei","Vier"};

    cout << "Container-Inhalt: ";
    std::copy(cont.begin(), cont.end(), std::ostream_iterator<Object>(cout,", "));
    cout << '\n';

    // Container umgekehrt in einen anderen Containertyp umkopieren
    // Der Ziel-Container enthält am Anfang keine Elemente, deshalb
    // muss für den Kopiervorgang ein entsprechender Inserter
    // verwendet werden

    std::deque<Object> revCont;
    std::copy(cont.begin(), cont.end(), std::front_inserter(revCont));
    cout << "Kopie in umgekehrter Reihenfolge: ";
    std::copy(revCont.begin(), revCont.end(), std::ostream_iterator<Object>(cout,", "));
    cout << '\n';

    // Tausche die ersten beiden Elemente des Ausgangs-Containers und
    // seiner umgekehrten Kopie gegeneinander aus

    std::swap_ranges(cont.begin(),cont.begin()+2,revCont.begin());
    cout << "Vertausche die beiden ersten Elemente der Container:\n"
         << "Ausgangs-Container: ";
    std::copy(cont.begin(), cont.end(), std::ostream_iterator<Object>(cout,", "));
    cout << "\nContainer-Kopie: ";
    std::copy(revCont.begin(), revCont.end(), std::ostream_iterator<Object>(cout,", "));
    cout << '\n';

    // Leeren Vektor definieren
    std::vector<int> intVect;
    // Vektor mit 5 Zufallszahlen füllen. generate_n() erzeugt die Zahlen
    // über Functor Randomize(). Da der Vektor noch leer ist, müssen die
    // die erzeugten Zahlen über einen Inserter eingefügt werden

    std::generate_n(std::back_inserter(intVect),5,Randomize(100));
    cout << "Vektor mit 5 Zufallszahlen: ";
    std::copy(intVect.begin(),intVect.end(),std::ostream_iterator<int>(cout,", "));
    cout << '\n';

    // Vektor fuer Differenzen definieren
    std::vector<int> diffVect;
    // Differenzen zwischen benachbarten Werten bilden und in neuem
    // Vektor ablegen. Der Trick dabei besteht darin, dass die Start-
    // positionen der beiden Input-Iteratoren um eins verschoben sind.
    // Damit wird die auszuführende Operation minus<> mit den Elementen
    // vector(n) und vector(n+1) aufgerufen. minus<> bildet dann aus diesen
    // beiden Werten die Differenz, die dann mit dem back_inserter in
    // den Differenz-Vektor übernommen wird.

    std::transform(intVect.begin(),intVect.end()-1,
                   intVect.begin()+1,
                   std::back_inserter(diffVect),std::minus<int>());
    cout << "Differenzen der benachbarten Elemente: ";
    std::copy(diffVect.begin(),diffVect.end(),std::ostream_iterator<int>(cout,", "));
    cout << '\n';

    // Differenzen kleiner < 0 durch 0 ersetzen
    std::replace_if(diffVect.begin(),diffVect.end(),std::bind2nd(std::less<int>(),0),0);
    cout << "Differenzen kleiner 0 durch 0 ersetzt: ";
    std::copy(diffVect.begin(),diffVect.end(),std::ostream_iterator<int>(cout,", "));
    cout << '\n';

    // int-Vektor leeren
    intVect.clear();
    // Differenzen umkopieren, 0 Differenzen dabei aber überspringen
    std::remove_copy(diffVect.begin(),diffVect.end(),std::back_inserter(intVect),0);
    cout << "Umkopierte Differenzen, ohne 0 Differenzen: ";
    std::copy(intVect.begin(),intVect.end(),std::ostream_iterator<int>(cout,", "));
    cout << '\n';
}
ProgrammausgabeContainer-Inhalt: Eins, Zwei, Drei, Vier,
Kopie in umgekehrter Reihenfolge: Vier, Drei, Zwei, Eins,
Vertausche die beiden ersten Elemente der Container:
Ausgangs-Container: Vier, Drei, Drei, Vier,
Container-Kopie: Eins, Zwei, Zwei, Eins,
Vektor mit 5 Zufallszahlen: 41, 67, 34, 0, 69,
Differenzen der benachbarten Elemente: -26, 33, 34, -69,
Differenzen kleiner 0 durch 0 ersetzt: 0, 33, 34, 0,
Umkopierte Differenzen, ohne 0 Differenzen: 33, 34,

Löschende Algorithmen

Folgende löschenden Algorithmen stehen zur Verfügung:

Algorithmus Beschreibung
remove() Entfernt Element mit einem bestimmten Wert
remove_if() Entfernt Element die einer bestimmten Bedingung genügen
remove_copy() Kopiert alle Elemente eines Bereichs die nicht einen bestimmten Wert besitzen
remove_copy_if() Kopiert alle Elemente eines Bereichs die nicht einer bestimmten Bedingung genügen
unique() Entfernt unmittelbar aufeinander folgende gleiche Werte
unique_copy() Kopiert alle Elemente, wobei aber unmittelbar aufeinander folgende Elemente mit dem gleichen Wert nur einmal kopiert werden

Die zu löschenden Elemente werden nur logisch entfernt, d.h. die Anzahl der Container-Elemente bleibt dabei erhalten. Es werden lediglich die zu löschenden Elemente mit den nachfolgenden Elementen überschrieben. Die löschenden Algorithmen liefern aber einen Iterator auf das neue logische Ende des Containers zurück. Sollen die 'überzähligen' Elemente entfernt werden, so ist die entsprechende erase(...) Memberfunktion des Containers aufzurufen (siehe dazu auch nachfolgendes Beispiel).

Beispiel zu den löschenden Algorithmen

PgmHeader// Beispiel zu Algorithmen
// Löschende Algorithmen


#include <iostream>
// Algorithmen einbinden
#include <algorithm>
// ostream_iterator einbinden
#include <iterator>
// vordefinierte Functors einbinden
#include <functional>
// Container einbinden
#include <vector>
#include <set>

using std::cout;
using std::endl;

// #define TRACE_METH
#include "object.h"

int main()
{
    // Container definieren/initialisieren
    using cType = std::vector<Object>;
    cType cont {"Eins","Zwei","Drei","Vier","Fuenf"};

    // Container spiegeln
    // Hier dürfen keine Iteratoren verwendet werden, da beim Spiegeln
    // des Containers eventuell neuer Speicher angefordert wird und
    // dies die Iteratoren ungültig macht!

    for (size_t index=cont.size(); index>0; index--)
        cont.push_back(cont[index-1]);
    cout << "Container-Inhalt:\n";
    std::copy(cont.begin(), cont.end(), std::ostream_iterator<Object>(cout,", "));
    cout << '\n';

    // Entferne alle Elemente mit dem Wert 'Drei'
    // remove() liefert neues logisches Ende des Containers zurück
    // Die Anzahl der Elemente bleibt nach dem Entfernen der Elemente
    // zunächst gleich
    // Iterator für neues logisches definieren

    auto newEnd = std::remove(cont.begin(), cont.end(), Object("Drei"));
    cout << "Elemente mit dem Wert 'Drei' entfernt:\n";
    std::copy(cont.begin(), cont.end(), std::ostream_iterator<Object>(cout,", "));
    cout << '\n';
    // Jetzt alle Elemente hinter dem neuen logischen Ende entfernen
    // (phyikalisches entfernen)

    cont.erase(newEnd,cont.end());
    cout << "Container auf neues logisches Ende verkuerzt:\n";
    std::copy(cont.begin(), cont.end(), std::ostream_iterator<Object>(cout,", "));
    cout << '\n';

    // Entferne alle unmittelbar aufeinander folgenden Elemente
    // die den gleichen Wert besitzen

    newEnd = std::unique(cont.begin(), cont.end());
    // Elemente physikalisch entfernen
    cont.erase(newEnd, cont.end());
    cout << "Alle unmittelbar aufeinander folgenden Werte entfernt:\n";
    std::copy(cont.begin(), cont.end(), std::ostream_iterator<Object>(cout,", "));
    cout << '\n';

    // Neuen Container definieren
    cType newCont;
    // Kopiere alle Elemente in einen neuen Container deren Wert
    // kleiner 'M' ist

    Object compObj("M");
    std::remove_copy_if(cont.begin(), cont.end(), std::back_inserter(newCont),
                        [&compObj](Object& obj)
                        {
                            return obj>=compObj;
                        });

    cout << "Element mit einem Wert kleiner 'M' umkopiert:\n";
    std::copy(newCont.begin(), newCont.end(), std::ostream_iterator<Object>(cout,", "));
    cout << '\n';
}
ProgrammausgabeContainer-Inhalt:
Eins, Zwei, Drei, Vier, Fuenf, Fuenf, Vier, Drei, Zwei, Eins,
Elemente mit dem Wert 'Drei' entfernt:
Eins, Zwei, Vier, Fuenf, Fuenf, Vier, Zwei, Eins, , ,
Container auf neues logisches Ende verkuerzt:
Eins, Zwei, Vier, Fuenf, Fuenf, Vier, Zwei, Eins,
Alle unmittelbar aufeinander folgenden Werte entfernt:
Eins, Zwei, Vier, Fuenf, Vier, Zwei, Eins,
Element mit einem Wert kleiner 'M' umkopiert:
Eins, Fuenf, Eins,