kramann.info
© Guido Kramann

Login: Passwort:










Informatik3
1 Vom_struct_zur_Klasse
..1.1 Vom_struct_zur_Klasse
..1.2 struct_Programm
..1.3 Klassen_Programm
..1.4 Offene_Fragen
..1.5 Historie
..1.6 Objektabstraktion
..1.7 OO_Kundenverwaltung
..1.8 Objektfaehigkeiten
..1.9 Formatierung
..1.10 Motivation
..1.11 Uebung1
..1.12 Uebung2
2 UML
..2.1 Volumenberechnung
..2.2 UML_Klassendiagramm
..2.3 Konstruktor
..2.4 Statische_Variable
3 Strings
..3.1 Klassenbibliotheken
..3.2 stringUML
..3.3 Uebung3
4 Initialisierungen
4 bluej
5 Zeiger_und_Arrays
..5.1 Zeiger
..5.2 Zeiger_und_Funktion
..5.3 Uebung4
6 Vererbung
..6.1 MesswerteUML
..6.2 MesswerteProgramm
..6.3 VererbungsProgramm
..6.4 Vector
..6.5 Uebung
7 Modifikatoren
..7.1 public_Vererbung
..7.2 protected_Vererbung
8 Listen_und_Templates
..8.1 Containertypen
....8.1.1 ListeUML
....8.1.2 ListeProgramm
..8.2 Templates
....8.2.1 Listentemplate
....8.2.2 STLvectorTemplate
..8.3 Uebung5
..8.4 Uebung6
..8.5 Uebung7
9 Java
..9.1 Uebung
..9.2 GettingStarted
..9.3 Animation
..9.4 Hybrid
..9.5 Threads
10 Delegation
11 LayoutProjekt
12 Fenster
13 Uebung
14 Zwischenprojekt
..14.1 Befehle
..14.2 Planung
..14.3 JNI
..14.4 JNIumsetzen
..14.5 Anwendungsklasse
..14.6 GUI01
..14.7 GUI02
15 Rasterlayout
..15.1 Bilder_Packages
..15.2 interfaces
..15.3 ArrayList
..15.4 clone
..15.5 Uebung
16 Nuetzliches
..16.1 Threads
..16.2 Animation
..16.3 RungeKutta
..16.4 Loesungsansatz
..16.5 Internetprogrammierung
....16.5.1 Codegenerierung
....16.5.2 PHP_Programmierung
....16.5.3 PHP_OOP
....16.5.4 Java
17 Algorithmen
..17.1 RungeKutta
..17.2 Loesungsansatz
..17.3 Evoopt
..17.4 Uebung12
..17.5 Uebung8_2014
..17.6 Ausdruecke
18 Uebung10
19 UML_ALT
..19.1 Flaechenberechnung
..19.2 UML_Flaechenberechnung
..19.3 Implementierung
..19.4 ListeUML
..19.5 ListenImplementierung
..19.6 Anwendung
kramann.info
© Guido Kramann

Login: Passwort:




Verwendung von Templates in den selbst geschriebenen KLassen Liste und ListenContainer

Hier zunächst ohne weiteren Kommentar die Lösung hierfür und ein paar Testbeispiele in der main-Methode (Zur Unterscheidung von der alten Version, wurde den Klassen ein T hintangestellt für "Template"):

Die KLasse ListeT mit Schablone für den Datentyp des Attributs daten

using namespace std;

template <typename meinDATENTYP>
class ListeT
{
    public: 
        ListeT()
        {
            nachfolger = 0;
        }
        
        ListeT(meinDATENTYP indaten)
        {
            daten = indaten;
            nachfolger = 0;
        }
        ListeT *nachfolger;
        meinDATENTYP daten;
};
 

Code 0-1: ListeT.h

Die KLasse ListenContainerT mit Schablone erzeugt auch Objekte von ListeT mit Schablonendatentyp

using namespace std;

template <typename meinDATENTYP>
class ListenContainerT
{
    private:
        ListeT<meinDATENTYP> kopfelement;
    public:
        ListenContainerT()
        {
            //Zunächst leere Liste erzeugen:
            kopfelement.nachfolger = 0;
        }

        //Übergebenes String in neues Listen-Element packen und dieses an das Ende der Liste hängen
        void append(meinDATENTYP daten)
        {
            ListeT<meinDATENTYP>* element = new ListeT<meinDATENTYP>(daten);
            ListeT<meinDATENTYP>* zeiger = &kopfelement;

            while(zeiger->nachfolger!=0)
                zeiger = zeiger->nachfolger;

            zeiger->nachfolger = element;
        }  

        //Refrerenz auf das i-te Listenelement suchen und dessen String-Inhalt zurückgeben
        meinDATENTYP get(int index)
        {
            ListeT<meinDATENTYP>* zeiger = kopfelement.nachfolger;

            for(int i=0;i<index;i++)
            {
                if(zeiger == 0)
                    return 0;
                zeiger = zeiger->nachfolger;
            }                    

            return zeiger->daten;      
        }

        //i-tes Listenelement löschen
        void del(int index)
        {
            //Hier muss der Zeiger auf das vorangehende und das nachfolgende Element
            //efunden werden.
            ListeT<meinDATENTYP>* zeiger     = kopfelement.nachfolger;
            ListeT<meinDATENTYP>* zeigervor  = &kopfelement;
            ListeT<meinDATENTYP>* zeigernach = 0;            

            for(int i=0;i<index;i++)
            {
                if(zeiger == 0)
                    return;
                zeigervor = zeiger;
                zeiger = zeiger->nachfolger;
            }                    
            zeigernach = zeiger->nachfolger;
            
            //Vorgänger-Element mit Nachfolger-Element verbinden:
            zeigervor->nachfolger = zeigernach;

            //Speicher für aktuelles Element freigeben:
            delete zeiger;
        }

        //die ganze Liste löschen.
        void clear()
        {                 
            ListeT<meinDATENTYP>* zeiger;
            ListeT<meinDATENTYP>* zeigervor  = &kopfelement;

            //Speicher bereinigen:
            while(kopfelement.nachfolger != 0)
            {
                zeiger    = kopfelement.nachfolger;
                zeigervor = &kopfelement;                
                while(zeiger->nachfolger != 0)
                {
                    zeigervor = zeiger;
                    zeiger = zeiger->nachfolger;
                }
                zeigervor->nachfolger=0;
                delete zeiger;
            }
        }

        //Liefert die Länge der Liste zurück
        int getSize()
        {
            int laenge = 0;
            ListeT<meinDATENTYP>* zeiger = &kopfelement;
            
            while(zeiger->nachfolger!=0)
            {
                laenge++;
                zeiger = zeiger->nachfolger;
            }

            return laenge;
        }
 };
 

Code 0-2: ListenContainerT.h

In testetemplate.cpp wird ListenContainerT für zwei ganz unterschiedliche Anwendungen eingesetzt

#include <string>
#include <iostream>

#include "ListeT.h"
#include "ListenContainerT.h"

using namespace std;

bool istTrennzeichen(char zeichen, string trennzeichen)
{
    for(int i=0;i<trennzeichen.size();i++)
    {
        if(zeichen == trennzeichen.at(i))
            return true;
    }
    return false;
}


void zerlegeText(ListenContainerT<string>* mLC,string text,string trennzeichen)
{
    int wortbeginn = 0;
    int wortende   = 0;

    for(int i=0;i<text.size();i++)
    {
        if(  !istTrennzeichen(text.at(i),trennzeichen) )
        {
            wortbeginn = i;
            while( i+1<text.size() && !istTrennzeichen(text.at(i+1),trennzeichen) )
                i++;

            wortende = i;
            mLC->append(text.substr(wortbeginn,wortende-wortbeginn+1));            
        }
    }
}


int main(void)
{

    string text = "Das ist ein Text.";        
    string trennzeichen = ".,;: ";
    ListenContainerT<string> mLC;

    zerlegeText(&mLC,text,trennzeichen);

    cout<<"GROESSE der Liste="<<mLC.getSize()<<endl;

    for(int i=0;i<mLC.getSize();i++)
        cout<<"Wort Nr."<<i<<":"<<mLC.get(i)<<endl;    

    ListenContainerT<double> zahlenliste;

    cout<<"Geben Sie beliebig viele Zahlen ein. Beenden Sie die Eingabe mit quit ENTER."<<endl;
    string eingabe;
    cin>>eingabe;
    while(eingabe!="quit")
    {
        zahlenliste.append(atof(eingabe.data()));
        cin>>eingabe;
    }

    cout<<"Folgende Zahlen wurden eingegeben:"<<endl;
    for(int i=0;i<zahlenliste.getSize();i++)
        cout<<zahlenliste.get(i)<<", ";
}
 

Code 0-3: testetemplate.cpp

testetemplate.zip

Erläuterungen zur Verwendung von Schablonen/Templates in C++ anhand des obigen Beispiels

  • Bei der hier benutzten Technik der Klassentemplates, wird der jeweiligen Klasse das Schlüsselwort template vorangestellt, gefolgt von spitzen Klammern, in denen das Schlüsselwort typename gefolgt von einem Bezeichner für den Platzhalter, der dort wo er auftaucht beim Erstellen eines Konkreten Objektes durch den gewünschten Datentyp ersetzt wird.
  • Innerhalb der Klasse wird dann überall dieser Bezeichner geschrieben, wo beim Erzeugen des Objekts der gewünschte Datentyp eingesetzt werden soll.
  • Das Erzeugen eines Objekts funktioniert dann, wie immer, nur dass hiner dem Klassennamen in sptitzen Klammern der einzusetzende Datentyp geschrieben wird.
  • Beispiele hierzu finden sich in testetemplate.cpp: ListenContainerT<double> zahlenliste;, ListenContainerT<string> mLC;, aber auch in der Klasse ListenContainer selbst, da diese ja Objekte von ListeT alegt, bzw. Zeiger auf Objekte dieses Typs: ListeT<meinDATENTYP>* zeiger = &kopfelement;, ListeT<meinDATENTYP> kopfelement;.
  • Es ist auch Möglich Templates zu schreiben, die mehrere Platzhalter ersetzen. Die entsprchende Kopfzeile: template<typename A, typename B >
  • Es ist auch Möglich Default-Typen anzugeben. Wenn dies für ein Argument geschieht, muss es auch für alle vorangehenden geschehen sein! - überlegen Sie warum. Die entsprchende Kopfzeile: template<typename A ,typename B = int, typename C = double >
  • Es ist auch möglich Templates nicht für ganze Klassen, sondern nur für einzelne Funktionen zu erstellen. Dies wird hier nicht behandelt.
  • Der Typ des Platzhalters, der in einem Template ersetzt wird, muss keinesfalls ein einfacher Datentyp sein. Es ist auch möglich Klassennamen zu verwenden und damit z.B. Objekte eines beliebigen Typs in unseren ListenContainer-Objekten zu sammeln.