Axel Rogat
Objektorientiertes Programmieren mit C++ und JAVA
 
9.2: Klassendefinitionen Kapitel 9 10.1: Member 
 
  9.3 C++-Version von intstack  
 

Wir setzen die Bibliothek zunächst 1:1 in eine Klasse um. Was die einzelnen Dateien angeht, sind wir nun in der Situation, die im Abschnitt 2.2.2 grafisch dargestellt wurde. Die Header-Datei zu unserer Klasse ist folgende:
(Quellcode)

// intstack.h -- Header-Datei zur Klassenbibliothek "intstack" class intstack { private: struct element *first; public: intstack(); bool isempty(); void push(int); void pop(); int top(); ~intstack(); };

Es folgt wieder ein kurzes Testprogramm:

(Quellcode)

#include <iostream.h> #include "intstack.h" void main(void) { intstack s; cout << "Stack ist" << (s.isempty()?" ":" nicht") << " leer\n"; s.push(1); cout << "Stack ist" << (s.isempty()?" ":" nicht") << " leer\n"; s.push(2); s.push(3); cout << "--> " << s.top() << endl; s.pop(); cout << "--> " << s.top() << endl; s.pop(); cout << "--> " << s.top() << endl; s.pop(); }
Es folgt die Ausgabe des Testprogramms von oben:
(intstack konstruiert) Stack ist leer Stack ist nicht leer --> 3 --> 2 --> 1 (intstack destruiert)
Der Konstruktor wird automatisch bei der Definition intstack s; aufgerufen, der Destruktor am Ende des Hauptprogramms, weil der Gültigkeitsbereich von s dort zu Ende ist.

Außer den erwarteten Ausgaben erhalten wir die beiden Zeilen (intstack konstruiert) und (intstack destruiert). Sie sind zum Testen in den Konstruktor, bzw. Destruktor eingebaut, wie wir unten sehen werden.

Die Implementationsdatei intstack.cpp sieht wie folgt aus:

(Quellcode)

/* intstack.cpp -- Implementations-Datei der Klassenbibliothek "intstack" */ #include "intstack.h" // eigene Header-Datei zur Sicherheit #include <iostream.h> // wegen cerr #include <stdlib.h> // wegen exit struct element // erst hier wird die interne { // Darstellung festgelegt int value; // Integer-Wert (eigentliche Daten) struct element *next; // Zeiger auf Folgeknoten }; static char *err_msg[]= { "pop bei leerem Stack", "top bei leerem Stack", "Speichermangel" }; enum err_codes { ERR_POP_EMPTY, ERR_TOP_EMPTY, ERR_MEMORY }; static void error(enum err_codes msg_nr) // interne Fehler-Routine { cerr << "FEHLER(intstack): " << err_msg[msg_nr] << endl; exit(10); } intstack::intstack() // Konstruktor -- anstelle stk_new { cout << "(intstack konstruiert)\n"; // unsere Test-Ausgabe first=0; // nur first auf 0 setzen } bool intstack::isempty() // Test auf 0 { return first==0; } void intstack::push(int n) // Element auf den Stack { element *new_elem; new_elem=new element; // neuen Knoten erzeugen if (new_elem==0) error(ERR_MEMORY); // Speichermangel... new_elem->value=n; // int eintragen new_elem->next=first; // Verkettung first=new_elem; // neue Stack-Spitze setzen } void intstack::pop() // Element herunterholen { element *old_top; // Hilfszeiger if (isempty()) error(ERR_POP_EMPTY); // pop bei leerem Stack old_top=first; // alte Spitze merkem first=first->next; // neue Stack-Spitze setzen delete old_top; // alte Spitze freigeben } int intstack::top() // oberstes Element abfragen { if (isempty()) error(ERR_TOP_EMPTY); // top bei leerem Stack return first->value; } intstack::~intstack() // Destruktor - anstelle stk_free { element *run1,*run2; // 2 Hilfszeiger run1=first; // Start bei Stack-Spitze while (run1!=0) // solange es noch Elemente gibt: { run2=run1->next; // nächstes Element merken delete run1; // aktuelles Element freigeben run1=run2; // weitergehen } first=0; // Stack auf leer setzen cout << "(intstack destruiert)\n"; // unsere Test-Ausgabe }
Wie schon bei den structs angedeutet, gibt es die Möglichkeit, Member-Funktionen direkt in der Klassendefinition mitzudefinieren -- sie sind dann automatisch inline. Das macht man meistens mit kurzen und einfachen Funktionen, um die Effizienz zu steigern. Der Nachteil ist, daß ihre Implementation dann in der Header-Datei offenbart wird. isempty und der Konstruktor sind in unserem Fall gute Kandidaten.

Die C++-Version ist zwar schon sicherer und leichter lesbar. Daß wir die wirklichen Vorteile noch nicht überschauen können, liegt daran, daß wir bisher im wesentlichen nur die C -Möglichkeiten auf C++ übertragen haben. Wir müssen in den nächsten Kapiteln die nötigen Hilfsmittel wie überladene Konstruktoren und Operatoren sowie Vererbung erst noch kennenlernen.

Nun gehen wir systematisch genauer auf die einzelnen Erscheinungen beim Klassenkonzept ein.

 
9.2: Klassendefinitionen Startseite 10.1: Member 
 

© 1998 Axel Rogat