C++ Kurs

Standard-Container III

Die Themen:

Einleitung
set und multiset
map und multimap
Beispiel und Übung

Einleitung

Wurden bisher nur Container behandelt, die die Elemente sequentiell (der Reihe nach) ablegen, so werden wir uns jetzt assoziative Container ansehen. Assoziativ-Container erlauben den schnellen Zugriff auf Elemente über einen frei wählbaren Schlüssel (Key), d.h. der direkt Zugriff auf die Elemente in einen Assoziativ-Container erfolgt nicht wie bei den sequentiellen Containern über einen Index oder Iterator sondern ausschließlich über einen Schlüssel. Die in einem Assoziativ-Container abzulegende Elemente müssen sortierbar sein. Das Sortierkriterium muss der 'strict weak ordering' Bedingung genügen:

Aber sehen wir uns nun die einzelnen Assoziativ-Container an:

set und multiset

Set bzw. Multiset definieren einen sortierten Container der nur den Schlüssel enthält. Der Unterschied zwischen einen Set und einem Multiset besteht darin, dass in einem Set ein Schlüssel nur einmal vorkommen darf während ein Multiset mehrere gleiche Schlüssel zulässt.

Typischerweise wird ein Set bzw. Multiset über einen binären Baum realisiert. Dies ist schreibt der C++ Standard aber nicht explizit vor

Wenn Sie ein Set bzw. Multiset einsetzen, müssen Sie die Header-Datei set einbinden. Der Datentyp eines Sets ist std:set<DTYP> und der eines Multisets std::multiset<DTYP>.

Wenn im Folgenden der Begriff Set verwendet wird, bezieht sich dies auch immer auf ein Multiset. Sollten zwischen einem Set und einem Multiset Unterschiede bestehen, wird darauf explizit hingewiesen.

Definition eines Set

Auch ein Set können Sie auf verschiedene Arten definieren (leeres Set, Set mit gleichen Elementen oder mit Elementen aus einem anderen Container initialisieren). Standardmäßig werden die Elemente in einem Set aufsteigend sortiert. Wollen Sie ein anderes Sortierkriterium, so müssen Sie dieses bei der Definition des Sets mit angeben. Das Sortierkriterium muss als Funktionsobjekt (Functor) definiert werden. Mehr zum Sortierkriterium gleich noch. Bitte beachten Sie, dass das Sortierkriterium mit zum Datentyp des Sets gehört!


#include <set>
// Leeres Set für ints definieren
set<int> emptySet;
// Set mit Sortierkriterium definieren
set<int,SortFtor> mySortSet;
// Set für Objekte definieren, Sortierkriterium ist
// innerhalb der Klasse definert
set<Object,Object::Sort> mySortObj;
// Elemente aus einer Liste in ein Set umkopieren
list<int> myList;
...
set<int> mySet(myList.begin(),myList.end());

Größenangaben

Über die Memberfunktion size() können Sie die Anzahl der Elemente in einem Set abfragen.

Elementzugriffe

Ein Set erlaubt keinen direkten Zugriff auf die Elemente. Der Zugriff kann nur über einen bidirektionalen Iterator oder über die gleich aufgeführten Such-Memberfunktionen erfolgen.


// Multiset definieren
multiset<int> mySet;
...
// Iterator definieren
multiset<int>::iterator iter;
// Alle Elemente auslesen
for (iter=mySet.begin(); iter!=mySet.end(); ++iter)
  cout << *iter << endl;

Beachten Sie, dass Sie auch hier einen bidirektionalen Iterator verwenden müssen, d.h. Sie können diesen Iterator nur inkrementieren bzw. dekrementieren. Wollen Sie den Iterator um n-Positionen verschieben, so müssen Sie dazu die Memberfunktion advance(...) verwenden.

Typdefinitionen

Keine Änderung gegenüber Vektor.

Einfügen und Entfernen von Elementen

Zum Einfügen und Entfernen von Elementen stehen die Memberfunktionen insert(...), erase(...) und clear() zur Verfügung.

Um ein einzelnes Element einzufügen, verwenden Sie die Memberfunktion insert(element). Sollen mehrere Elemente aus einem anderen Bereich eingefügt werden, so ist insert(beg,end) zu verwenden. beg ist ein Iterator der die Startposition der einzufügenden Elemente angibt und end der Iterator auf das Ende+1 der einzufügenden Elemente. Beachten Sie, dass das Element an der Position end nicht mehr übernommen wird.

Zum Löschen eines einzelnen Elements dient die Memberfunktion erase(element) und zum Löschen mehrere Elemente die Memberfunktion erase(beg,end). Das Element an der Position end verbleibt weiterhin im Set. Bei einem Multiset entfernt erase(element) alle Elemente, die mit dem übergebenen übereinstimmen.

Die Returntypen der Memberfunktionen insert(element) unterscheiden sich bei einem Set und einem Multiset, da bei einem Set ja keine doppelten Schlüssel auftreten dürfen und damit die insert(element) Memberfunktion auch fehlschlagen kann.

Die insert(element) Memberfunktion eines Sets besitzt den Returntyp

pair<set<DTYP>::iterator,bool>

Das erste pair-Element ist ein Iterator auf die Einfügeposition des Elements und das zweite pair-Element gibt den Erfolg/Misserfolg der Operation an. Enthält das zweite pair-Element den Wert false, so konnte das Element nicht eingefügt werden.


// Set definieren
set<int> mySet;
...
// pair-Return für insert definieren
pair<set<int>::iterator,bool> success;
// Element einfügen
success = mySet.insert(value);
// Abprüfen ob Element eingefügt wurde
if (!success.second)
  ... // Fehlerbehandlung

Zuweisungen, Vergleichsoperationen und Iteratoren

Hierbei gibt es keine Unterschiede zur Liste (Iteratoren sind bidirektional). Beachten müssen Sie nur, dass Sie nur gleiche Sets einander zuweisen können. Ein Set mit dem Sortierkriterium A ist nicht identisch mit einem Set mit dem Sortierkriterium B, d.h. das Sortierkriterium gehört mit zum Datentyp des Sets.

Weitere Set-Memberfunktionen

Sets sind Container die zum schnellen Suchen von Elementen optimiert sind. Daher stehen zum Suchen entsprechende Memberfunktionen bereit.

Die Memberfunktion count(element) liefert die Anzahl der Elemente mit dem Wert element zurück. Bei einem set kann die Memberfunktion nur 0 oder 1 zurückliefern.

Soll die (erste) Position eines Wertes im einem Set bestimmt werden, so steht dazu die Memberfunktion find(element) zur Verfügung. find(...) liefert einen Iterator auf das gesuchte Element zurück. Ist das gesuchte Element nicht im Container vorhanden, so liefert find(...) die Position end() zurück.


multiset<int> mySet;
...
// Anzahl der Elemente mit dem Wert 5
size_t noOfElements = mySet.count(5);
// Iterator definieren
multiset<int>::iterator iter;
// Erstes Element mit dem Wert 10 suchen
iter = mySet.find(10);
// Wenn das Element nicht vorhanden ist
if (iter == mySet.end())
  ...

Zusätzlich zum Suchen eines bestimmten Wertes enthält ein Set noch die Memberfunktionen lower_bound(element), upper_bound(element) und equal_range(element) zum Suchen von Wertebereichen. Alle Memberfunktionen liefern Iteratoren auf die gefundenen Positionen innerhalb des Sets zurück. lower_bound(element) sucht nach der Position, die einen Wert größer/gleich element besitzt während upper_bound(element) die erste Position liefert, die einen Wert größer als element besitzt. equal_range(element) ist eine Kombination aus lower_bound(...) und upper_bound(...) und liefert die beiden Positionen in einem pair zurück.


multiset<int> mySet;
...
// Iteratoren definieren
typedef multiset<int>::iterator msi;
msi liter, uiter;
// Erste Position des Werts 10 oder größer suchen
liter = mySet.lower_bound(10);
// Letzte+1 Position des Wertes größer 10 suchen
uiter = mySet.upper_bound(10);
// Beide Position auf einmal holen
pair<msi,msi> range = mySet.equal_range(10);

Sehen wir uns einmal ein praktisches Beispiel für den Einsatz eines sets an.

Beispiel:

Das Beispiel zeigt die Anwendung eines Sets als 'Wörterbuch'. Dazu werden zunächst aus einer Datei alle Wörter eingelesen und in einem set abgelegt. Da die Wörter unabhängig von ihrer Groß-/Kleinschreibung im Wörterbuch abgelegt werden sollen, erhält das set ein eigenes Sortierkriterium ignorecase.

Werden beim Einfügen der Wörter in das set bereits eingefügte Wörter gefunden, so wird eine entsprechende Meldung ausgegeben.

Sind alle Wörter aus der Datei im set abgelegt, wird das komplette Wörterbuch zunächst ausgegeben.

Anschließend werden alle Wörter die mit einem bestimmten Buchstaben beginnen (im Beispiel 'e') gesucht. Dazu wird zunächst die Position des ersten und letzten Wortes mit dem Anfangsbuchstaben mit der Memberfunktion lower_bound(...) bzw. upper_bound(...) bestimmt. Beide Memberfunktionen liefern entsprechende Iteratoren zurück, die dann für die Ausgabe der gesuchten Wörter verwendet werden.

Bevor Sie das Beispiel ausführen können, müssen Sie noch die Datei demo.txt mit folgendem Inhalt anlegen:


In der letzten Lektion haben Sie etwas ueber Funktionen und Templates erfahren
Aber Sie koennen Templates nicht nur fuer Funktionen verwenden sondern auch fuer Klassen einsetzen

Woerter aus einer Datei im Set ablegen:
Wort 'Sie' schon vorhanden!
Wort 'Templates' schon vorhanden!
Wort 'Funktionen' schon vorhanden!
Wort 'fuer' schon vorhanden!
Wort 'verwenden' schon vorhanden!
In der Datei enthaltene Worte: Aber,auch,der,einsetzen,erfahren,etwas,fuer,Funktionen,haben,In,Klassen,koennen,
Lektion,letzten,nicht,nur,Sie,sondern,Templates,ueber,und,verwenden,
Alle Woerter mit dem Buchstaben 'e':
Anzahl: 3 einsetzen erfahren etwas
Anzahl der Woerter (ohne Duplikate): 22


// Beispiel zu set

#include <iostream>
#include <fstream>
#include <set>
#include <string>
#include <locale>

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

// Name der zu untersuchenden Textdatei
const char *pFILE = "demo.txt";

// Functor, Stringvergleich ohne Berücksichtigung der
// Gross-/Kleinschreibung
struct ignorecase
{
    std::locale loc;
    bool operator()(const string& s1, const string& s2) const
    {
        string::const_iterator iter1 = s1.begin();
        string::const_iterator iter2 = s2.begin();
        while ((iter1!=s1.end()) && (iter2!=s2.end()))
        {
            // tolower aus locale. Berücksichtigt die
            // landesspezifischen Umwandlungen
            char cs1 = std::tolower(*iter1,loc);
            char cs2 = std::tolower(*iter2,loc);
            // Buchstaben vergleichen
            if (cs1<cs2)
                return true;
            if (cs1>cs2)
                return false;
            // Bei Gleichheit, nächster Buchstabe
            ++iter1;
            ++iter2;
        }
        return false;
    }
};

// main() Funktion
int main()
{
    // Zu untersuchende Datei öffnen
    std::ifstream infile(pFILE);
    if (!infile)
    {
        cout << "Fehler beim Oeffnen der Datei " << pFILE << endl;
        exit(1);
    }

    // Set für Wörter aus Datei anlegen
    // Ignoriert Gross/Kleinschreibung beim Einfügen über Functor ignorecase()
    std::set<string,ignorecase> dictionary;
    // Returncode für insert() Memberfunktion
    std::pair<std::set<string,ignorecase>::iterator,bool> success;
    // Iterator definieren
    std::set<string,ignorecase>::iterator iter;

    // Komplette Datei einlesen
    cout << "Woerter aus einer Datei im Set ablegen:\n";
    while (!infile.eof())
    {
        // Wort aus Datei einlesen
        string word;
        infile >> word;
        // und im Set ablegen. Falls Wort schon vorhanden, Meldung ausgeben
        success = dictionary.insert(word);
        if (!success.second)
            cout << "Wort '" << *success.first << "' schon vorhanden!\n";
    }
    // Alle Wörter aus Datei ausgeben
    cout << "In der Datei enthaltene Worte:\n";
    for (iter=dictionary.begin(); iter!=dictionary.end(); ++iter)
        cout << *iter << ',';
    cout << endl;

    // Nun alle Wörter mit einem bestimmten Buchstaben ausgeben
    // Gesuchter String
    string toSearch = "e";
    // Iterator auf ersten übereinstimmenden Eintrag
    std::set<string,ignorecase>::iterator lowerPos = dictionary.lower_bound(toSearch);
    // Iterator auf ersten nicht mehr übereinstimmenden Folgeeintrag
    std::set<string,ignorecase>::iterator upperPos = dictionary.upper_bound(toSearch);
    // Alle gefunden Wörter ausgeben
    cout << "Alle Woerter mit dem Buchstaben 'e':\n";
    cout << "Anzahl: " << std::distance(lowerPos,upperPos) << endl;
    for (iter=lowerPos; iter!=upperPos; ++iter)
        cout << *iter << ' ';
    cout << endl;
    cout << "Anzahl der Woerter (ohne Duplikate): " << dictionary.size() << endl;
}

Sie sehen, mithilfe eines solchen Sets lassen sich auf elegante Art und Weise sortierte Reihenfolgen erstellen und verarbeiten. Ein solches Set kann z.B. auch zum Sortieren und Verarbeiten einer Adressenliste verwendet werden, wenn das Sortierkriterium entsprechend definiert ist. Denken Sie immer daran, das die Container nicht nur einfache Daten sondern auch Objekte aufnehmen können. Im obigen Beispiel enthält das set z.B. Objekte vom Typ string.

map und multimap

Map bzw. Multimap definieren einen sortierten Container, der nun nicht nur einen Schlüssel enthält sondern auch einen beliebigen zusätzlichen Wert. Der Unterschied zwischen einer Map und einer Multimap besteht, genauso wie beim Set, darin, dass in einer Map ein Schlüssel nur einmal vorhanden sein darf und eine Multimap mehrere gleiche Schlüssel zulässt.

Typischerweise wird eine Map bzw. Multimap über einen binären Baum realisiert. Dies schreibt der C++ Standard aber nicht explizit vor.

Wenn Sie eine Map bzw. Multimap einsetzen, müssen Sie die Header-Datei map einbinden. Der Datentyp einer Map ist std:map<DTYP> und der einer Multimap std::multimap<DTYP>.

Wenn im Folgenden der Begriff Map verwendet wird, bezieht sich dies auch immer auf ein Multimap. Sollten zwischen einer Map und eine Multimap Unterschiede bestehen, wird darauf explizit hingewiesen.

Definition einer Map

Da eine Map zusätzlich zum Schlüssel noch einen beliebigen Wert enthält, muss der Datentyp dieses zusätzlichen Werts mit bei der Definition angegeben werden. Standardmäßig werden die Elemente in einer Map aufsteigend sortiert. Wollen Sie ein anderes Sortierkriterium, so müssen Sie dieses bei der Definition der Map mit angeben. Das Sortierkriterium ist, wie bei einem Set, über ein entsprechendes Funktionsobjekt zu definieren.


#include <map>
// Leere Map mit int-Schlüssel und string als zusätzliches Datum
map<int, string> emptyMap;
// Gleiche Map mit zusätzlichem Sortierkriterium definieren
map<int,string,SortFtor> mySortMap;

Größenangaben

Über die Memberfunktion size() können Sie die Anzahl der Elemente in einer Map abfragen.

Elementzugriffe

Im Gegensatz zu einem Set, erlaubt eine Map zusätzlich den direkten indizierten Zugriff auf die Elemente, wobei als Index der Schlüssel anzugeben ist. Bei dieser Zugriffsart kann der Schlüssel nicht verändert werden sondern nur der mit ihm verbundene Wert. Beachten Sie im Beispiel, dass der Schlüssel vom Typ string ist und damit innerhalb der Indexklammern auch ein string (oder etwas in einen string konvertierbares) stehen muss.


// Map definieren, string ist Schlüssel und Zusatzwert ist int
map<string,int> myMap;
...
// Zusatzwert direkt setzen
myMap["KEY"] = 10;
// Zusatzwert auslesen
int var = myMap["KEY"];

Geben Sie beim Setzen des Zusatzwertes einen nicht existierenden Schlüssel an, so wird ein neues Elemente mit diesem Schlüssel erzeugt und in die Map eingefügt. Wird beim Auslesen eines Elements ein nicht vorhandener Schlüssel angegeben, wird ebenfalls ein neues Element erstellt und der Zusatzwert mit seinem Standard-Konstruktor initialisiert.

Greifen Sie über einen Iterator auf Elemente in einer Map zu, so erhalten Sie als Rückgabewert immer ein pair vom Typ pair<key,value>. Alternativ können Sie auch nur den Schlüssel bzw. den Wert wie unten angegeben auslesen. Ebenfalls möglich ist es, den Wert über einen Iterator neu zu setzen. Nicht verändern können Sie aber den Schlüssel, da dies ansonsten die Sortierung der Map durcheinander bringen würde.


// Map definieren, string ist Schlüssel und Zusatzwert ist int
typedef map<string,int> smap;
smap myMap;
// Iterator definieren/initialisieren
smap::iterator iter = myMap.begin();
// komplettes Element auslesen
pair<string,int> rval = *iter;
// Nur Key auslesen
string sret = iter->first;
// Nur Wert auslesen
int val = iter->second;
// Wert über Iterator setzen
iter->second = 10;

Typdefinitionen

Identisch mit denen des Vektors.

Einfügen und Entfernen von Elementen

Es stehen die gleichen Memberfunktionen insert(...), erase(...) und clear() wie beim Set zur Verfügung.

Beachten müssen Sie hier, dass ein Map-Element immer aus einem Schlüssel/Wert Paar besteht. Sie müssen also der insert(element) Memberfunktion, wie nachfolgend dargestellt, ein solches Paar übergeben. Im Beispiel sehen Sie drei verschiedene Weisen, wie Sie ein Element in eine Map einfügen können.


// Multimap definieren
typedef multimap<int, string> mis;
mis myMap;
// Neue Elemente einfügen
myMap.insert(pair<int,string>(10,"ten"));
myMap.insert(make_pair(11,"eleven"));
myMap.insert(mis::value_type(9,"nine"));

Bezüglich des Returnwertes der insert(element.) Memberfunktion für eine Map gilt das Gleiche wie beim Set. Da eine einfache map keine doppelten Schlüssel zulässt, liefert die insert(element) Memberfunktion ebenfalls ein pair zurück, dessen zweites Element (second) den Erfolg oder Misserfolg der Operation angibt.

Zuweisungen, Vergleichsoperationen und Iteratoren

Auch hier gilt das Gleiche wie beim Set.

Weitere Map-Memberfunktionen

Auch Maps sind Container die zum schnellen Suchen von Elementen optimiert sind. Und daher stehen zum Suchen die gleichen Memberfunktionen count(key), find(key), lower_bound(key), upper_bound(key) und equal_range(key) wie beim Set zur Verfügung. Als Parameter erhalten alle Memberfunktionen nur den Schlüssel des zu suchenden Elements übergeben.

Damit haben Sie alle Container der Standard Bibliothek mit ihren wichtigsten Eigenschaften und Operationen kennen gelernt. Im Anschluss finden Sie eine Tabelle mit einer Übersicht über alle Container.

Beispiel und Übung

Beispiel:

Das Beispiel simuliert die Verwaltung eines Aktienkontos. Da häufig nach bestimmten Aktien gesucht werden soll und eventuell deren Kurs zu berichtigen ist, wir eine entsprechende map eingesetzt. Es reicht hier eine einfache map, da keine Aktien doppelt vorkommen sollen.

Ein Eintrag im Aktienkonto besteht dabei aus dem Namen der Aktie und dem aktuellen Kurs. Der Aktienname ist als string implementiert und der Kurs als float-Wert.

Damit Sie auch einmal sehen, dass sich Container auch dynamisch anlegen lassen, wird das Aktienkonto aktien in main() dynamisch angelegt.

Anschließend werden einige Aktien zum Aktienkonto hinzugefügt. Hierzu wird der indiziert Zugriff auf eine map verwendet. Das schöne an diesem Zugriff ist, dass Sie zum einen den Namen der Aktie im Klartext angegeben können (als string) und zum anderen, dass bei eventuell noch nicht vorhandener Aktie diese automatisch angelegt wird.

Wie weiter vorne im Kurs aber bereits schon erwähnt, birgt das automatische Anlegen auch eine gewisse Gefahr. Haben Sie sich nämlich nur 'verschrieben', so wird auch in diesem Fall ein neuer Eintrag in die map eingefügt.

Im Anschluss an das Einfügen der Aktien werden diese zur Kontrolle alle ausgegeben.

Danach wird der Kurswert einer Aktien erhöht, eine neue Aktie hinzugefügt und das ganze Aktienkonto dann erneut ausgegeben.

Zum Schluss wird der Name einer Aktie umbenannt. Da der Aktienname als Schlüssel innerhalb der map verwendet wird, kann dies nicht direkt erfolgen, da ansonsten die map nicht mehr sortiert wäre. Im Beispiel wird dazu die umzubenennende Aktie einer neuen Aktie zugewiesen. Bei einer solchen Zuweisung wird nur der Wert und nicht der Schlüssel übernommen, d.h. es wird lediglich der Aktienkurs übernommen. Anschließend wird die alte, bisherige Aktie gelöscht.

BMW Aktie: 54.45 EUR
Daimler Crysler Aktie: 123.50 EUR
Ford Aktie: 34.45 EUR
VW Aktie: 12.00 EUR
-- VW Aktie stiegt um 10.00 EUR --
BMW Aktie: 54.45
Daimler Crysler Aktie: 123.50
Ford Aktie: 34.45
VW Aktie: 22.00
-- Porsche Aktien hinzufuegen --
-- Daimler Crysler in Daimler umbenennen --
BMW Aktie: 54.45
Daimler Aktie: 123.50
Ford Aktie: 34.45
Porsche Aktie: 560.00
VW Aktie: 22.00


// Beispiel zu map

#include <iostream>
#include <iomanip>
#include <string>
#include <map>

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

// main() Funktion
int main()
{
    // Zur Abwechslung mal ein Zeiger auf einen Container!
    typedef std::map<std::string,float> aktien;
    aktien *pAktien;
    // Iterator definieren
    aktien::iterator iter;

    // Map dynamisch anlegen
    pAktien = new aktien;

    // Map mit Daten füllen
    (*pAktien)["VW"] = 12.f;
    (*pAktien)["Ford"] = 34.45f;
    (*pAktien)["BMW"] = 54.45f;
    (*pAktien)["Daimler Crysler"] = 123.50f;

    // Ausgabe der Aktienwerte mit 2 Nachkommastellen
    cout << std::fixed << std::setprecision(2);
    // Alle Aktien ausgeben
    for (iter=pAktien->begin(); iter!=pAktien->end(); ++iter)
        cout << iter->first << " Aktie: " << iter->second << " EUR\n";

    // VW Aktien um 10.00 erhöhen
    cout << "-- VW Aktie stiegt um 10.00 EUR --\n";
    (*pAktien)["VW"] += 10.f;
    // Alle Aktien ausgeben
    for (iter=pAktien->begin(); iter!=pAktien->end(); ++iter)
        cout << iter->first << " Aktie: " << iter->second << endl;

    // Element explizit hinzufügen
    cout << "-- Porsche Aktien hinzufuegen --\n";
    pAktien->insert(std::pair<const std::string,float>("Porsche",560.0f));
    // Daimler Crysler Aktien in Daimler umbenennen
    // Zuerst kopieren
    cout << "-- Daimler Crysler in Daimler umbenennen --\n";
    (*pAktien)["Daimler"] = (*pAktien)["Daimler Crysler"];
    // und dann löschen!
    pAktien->erase("Daimler Crysler");
    // Alle Aktien ausgeben
    for (iter=pAktien->begin(); iter!=pAktien->end(); ++iter)
        cout << iter->first << " Aktie: " << iter->second << endl;

    // map wieder löschen!
    delete pAktien;

}

Übung:

In dieser Übung geht es darum, eine Klasse zu implementieren deren Schnittstellen bereits vorgegeben sind. Ziel ist es, eine Klasse für die Verwaltung von Adressen zu implementieren. Die Adressdaten bestehen dabei aus einem Namen, einer Telefon-Nummer und einer Email-Adresse. Alle Adressdaten liegen in Form von Strings vor.

Da sehr häufig Änderungen/Erweiterungen am Adressbuch vorgenommen werden, sollen die Adressdaten in einem entsprechenden Container abgelegt werden. Doppelte Adressen sind nicht zugelassen. Als Suchkriterium dient der Name.

Vervollständigen Sie die Implementierung der nachfolgenden Klasse AddressBook.

Die main() Funktion zum Testen der Klasse sowie der prinzipielle Klassenaufbau des Adressbuchs ist bereits fertig vorgegeben, d.h. bei richtiger Implementierung sollten Sie die nachfolgende Ausgabe erhalten.

Adressbuch enthaelt 4 Eintraege
Name: Agathe Maier
Tel : 0111/4545, Email: agma@sonst.net
Name: Emil Maier
Tel : 0111/4545, Email: ema@sonst.net
Name: Franzi Xelsbrot
Tel : 08888/1234, Email: frax@kostnix.de
Name: Gustav Gans
Tel : 09999/1111, Email: guga@lucky.com
Aendere Telefon-Nummer von Gustav Gans:
Neuer Eintrag ist:
Name: Gustav Gans
Tel : 09999/7777, Email: guga@lucky.com
Entferne Franzi Xelsbrot:
Neues Adressbuch enthaelt 3 Eintraege:
Name: Agathe Maier
Tel : 0111/4545, Email: agma@sonst.net
Name: Emil Maier
Tel : 0111/4545, Email: ema@sonst.net
Name: Gustav Gans
Tel : 09999/7777, Email: guga@lucky.com


// Loesung zu Container

#include <iostream>
#include <string>
#include <map>
#include <iterator>

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

// Adressbuch Klasse
// Lässt keine doppelten Einträge zu
class AddressBook
{
    // *** hier den Container definieren ***
  public:
    // ctor
    AddressBook()
    {}
    // dtor
    ~AddressBook()
    {}
    // Neue Adresse aufnehmen
    void Insert(const string& name, const string& phone, const string& email);
    // Gibt einzelne Adresse aus
    void PrintAddress(const string& name);
    // Ändert Adressbuch-Eintrag
    bool Update(const string& name, const string& phone, const string& email);
    // Adresse entfernen
    void Remove(const string& name);
    // Liefert Anzahl der Adressbuch-Einträge
    size_t size()
    {

    }
    // Gibt gesamtes Adressbuch aus
    friend std::ostream& operator << (std::ostream& os, AddressBook& abook);
};
// Ausgabe einer Adresse
void AddressBook::PrintAddress(const string& name)
{

}
// Neuen Eintrag ins Adressbuch aufnehmen
void AddressBook::Insert(const string& name, const string& phone, const string& email)
{

}
// Eintrag aus Adressbuch entfernen
void AddressBook::Remove(const string& name)
{

}
// Eintrag abändern
bool AddressBook::Update(const string& name, const string& phone_, const string& email_)
{

}
// Ausgabe des gesamten Adressbuchs
// I.A. kann noch kein const-Objekt übergeben werden da hierfür ein anderer,
// (noch nicht behandelter) Iteratortyp (const_iterator) benötigt wird
std::ostream& operator << (std::ostream& os, AddressBook& abook)
{

}

// main() Funktion
int main()
{
    // Adressbuch dynamisch anlegen
    AddressBook *pAddr = new AddressBook;

    // Einige Einträge hinzufügen
    pAddr->Insert("Emil Maier", "0111/4545", "ema@sonst.net");
    pAddr->Insert("Agathe Maier", "0111/4545", "agma@sonst.net");
    pAddr->Insert("Franzi Xelsbrot", "08888/1234", "frax@kostnix.de");
    pAddr->Insert("Gustav Gans", "09999/1111", "guga@lucky.com");

    // Komplettes Adressbuch ausgeben
    cout << "Adressbuch enthaelt " << pAddr->size() << " Eintraege\n";
    cout << *pAddr;

    // Eintrag abändern
    cout << "Aendere Telefon-Nummer von Gustav Gans:\n";
    pAddr->Update("Gustav Gans", "09999/7777", "guga@lucky.com");
    // Und nur geänderten Eintrag ausgeben
    cout << "Neuer Eintrag ist:\n";
    pAddr->PrintAddress("Gustav Gans");

    // Eintrag entfernen
    cout << "Entferne Franzi Xelsbrot:\n";
    pAddr->Remove("Franzi Xelsbrot");

    // Nochmals komplettes Adressbuch ausgeben
    cout << "Neues Adressbuch enthaelt " << pAddr->size() << " Eintraege:\n";
    cout << *pAddr;
    
    // Adressbuch löschen
    delete pAddr;
}

Lösung ansehen!