320 likes | 404 Views
Gliederung. Ausnahmebehandlung Grundphänomene Am Beispiel Konto: 3 Varianten Möglichkeiten bei der Verwendung von Klassenobjekten 4 Varianten. Beispiel: Kontoführung. Ich kann ein Konto eröffnen, auf ein Konto einzahlen, von einem Konto abheben, ein Konto überziehen,
E N D
Gliederung • Ausnahmebehandlung • Grundphänomene • Am Beispiel Konto: 3 Varianten • Möglichkeiten bei der Verwendung von Klassenobjekten • 4 Varianten
Beispiel: Kontoführung • Ich kann • ein Konto eröffnen, • auf ein Konto einzahlen, • von einem Konto abheben, • ein Konto überziehen, • ein Konto wieder schließen. • (Abstrakter Datentyp)
Klasse EinfachesKonto class Konto { protected: int stand; public: Konto(){stand = 0;} ~Konto(){ cout << "Nachricht" << endl; } void Einzahlen(int k) {stand += k;} int Abheben(int k) { stand -= k; return k; } int KontoStand() {return stand;} };
Klasse EinfachesKonto (Forts.) int main() { int i, j, st; Konto * kto = new Konto; for (i = 0; i < 5; i++) { j = rand(); cout << "\nEingezahlt: \t" << j; kto ->Einzahlen(j); cout << ",\tAbgehoben:\t" << kto ->Abheben((i+1)*rand()); st = kto ->KontoStand(); cout << ",\tKontostand:\t" << st << (st < 0? " *": ""); } kto ->~Konto(); } Konto1
Klasse EinfachesKonto (Forts.) Eingezahlt: 16838, Abgehoben: 5758, Kontostand: 11080 Eingezahlt: 10113, Abgehoben: 35030, Kontostand: -13837 * Eingezahlt: 31051, Abgehoben: 16881, Kontostand: 333 Eingezahlt: 23010, Abgehoben: 29676, Kontostand: -6333 * Eingezahlt: 16212, Abgehoben: 20430, Kontostand: -10551 * Konto bei Geschaeftsschluss aufgeloest
Überziehung des Kontos • Wie wird das behandelt? • Annahme: • Die Überziehung hat Konsequenzen: • Konto wird dann aufgelöst. • Möglichkeiten: • nach jeder Abhebung wird gefragt, ob das Konto überzogen ist, • ja: Konto auflösen, Abbruch • nein: weitermachen wie gewohnt.
Überziehung des Kontos • ist für den Code umständlich: • Der Normalfall und der Ausnahmefall werden ineinander vermischt behandelt • Alternative: Ausnahmebehandlung • Wenn die Ausnahmesituation entsteht, wird dem benutzen-den Programmteil gesagt: • „Hier stimmt etwas nicht, es muß etwas geschehen“ • d.h. Ausnahmebehandlung
Modifikation der Klasse class Konto { protected: int stand; public: Konto(){stand = 0;} ~Konto(){cout << "Nachricht";} void Einzahlen(int k) {stand += k;} int Abheben(int k) {stand -= k; if (stand < 0) throw k; return k; } int KontoStand() {return stand;} };
Interpretation • Wenn diese Bedingung stand < 0 erfüllt ist, aktiviere eine Ausnahme mit dem Wert k: (das ist der Betrag, bei dessen Abheben das Konto überzogen wurde) • Die Ausnahme muß irgendwo behandelt werden: (d.h. zum throw gehört ein catch)
... der Fänger Konto * kto = new Konto; try{ for (i = 0; i < 5; i++) { cout << "\nEingezahlt: \t" << ...; kto->Einzahlen(...); abh = kto->Abheben(...); cout << ",\nabgehoben: \t" << abh; st = kto->KontoStand(); cout << ",\tKontostand:\t" << st << (st < 0? " *": ""); } } catch(int t) { cout << "\nKonto ueberzogen .." << t << "DM\n"; kto->~Konto(); } Konto * kto = new Konto; for (i = 0; i < 5; i++) { cout << "\nEingezahlt: \t" << ...; kto->Einzahlen(...); abh = kto->Abheben(...); cout << ",\nabgehoben: \t" << abh; st = kto->KontoStand(); cout << ",\tKontostand:\t" << st << (st < 0? " *": ""); } Programm
Ausgabe Eingezahlt: 16838, abgehoben: 5758, Kontostand: 11080 Eingezahlt: 10113 Konto ueberzogen beim Abheben von 35030 DM Konto aufgeloest Hier wurde die Ausnahme offenbar ausgelöst
Anmerkungen • Der Aufruf einer Methode, die eine Ausnahme auslöst, muß in einem Block stattfinden, der mit try eingeleitet wird, also: try { ... Methode oder Funktion, die eine Ausnahme auslöst ... }
Anmerkungen • Unmittelbar auf try folgt ein oder folgen mehrere catch zur Behandlung der Ausnahme: • catch ist parametrisiert: Der formale Parameter wird beim Behandeln der Ausnahme durch einen aktuellen Parameter ersetzt, der von throw gesetzt wird. • Analogie: • Funktionsvereinbarung: Arbeit mit formalen Parametern • Funktionsaufruf: Substitution durch aktuelle Parameter
Anmerkungen • Kontrollfluß bei der Aktivierung einer Ausnahme: • Aktivieren der Ausnahme mitthrow <WurfGeschoß> • Suchen der passenden catch-Klausel • Ausführen des entsprechenden Blocks (mit Wurf-Geschoß als aktuellem Parameter) • Danach Verlassen der catch-Klausel und Weiterar-beit mit dem Block, der unmittelbar auf die catch-Klauseln folgt • Insbesondere: Keine Rückkehr in den try-Block oder die aktivierende Prozedur
Beispiel (Forts.) • Verdacht auf Steuerbetrug, wenn ein zu hoher Betrag eingezahlt wird. --> Aktivierung einer Ausnahme. • Jedoch Problem: Kann die Ausnahme nicht mit throw k aktivieren, da die beiden Situationen auf diese Art nicht voneinander getrennt werden können. • Es gilt aber: Es können Instanzen beliebiger Klassen geworfen werden.
Beispiel (Forts.) • Definiere zwei Klassen • Ueberziehung • VerdachtSteuerbetrug • Wirf entsprechende Ausnahmen • Behandle die Ausnahmen nach den Typen der geworfenen Argumente, also: • Ueberziehung könnte anders behandelt werden als VerdachtSteuerbetrug • z. B.: bei Ueberziehung schließen wir das Konto
Modifiziertes Konto class Ueberziehung { public: int wert; Ueberziehung(int j) { wert = j; } }; class VerdachtSteuerbetrug { public: int wert; VerdachtSteuerbetrug(int z) { wert = z; } }; eine ganz normale Klasse noch eine ganz normale Klasse
Modifiziertes Konto (Forts.) void Einzahlen(int k) { stand += k; if (k > 30000) throw new VerdachtSteuerbetrug(k); } int Abheben(int k) { stand -= k; if (stand < 0) throw new Ueberziehung(k); return k; } hier werden die entsprechenden Instanzen geworfen
Behandlung der Ausnahmen catch(Ueberziehung * t) { cout << "\nKonto ueberzogen beim Abheben von " << t->wert << " DM\n"; kto->~Konto(); } catch (VerdachtSteuerbetrug * r) { cout << "\nZu hohe Einzahlung. Steuerbetrug? " << r->wert << endl; } Konto3.cpp Programm
Ausgaben • Zweite Einzahlung: 31340 • Eingezahlt: 17000, • Abgehoben: 16838, Kontostand: 162 • Eingezahlt: 31340 • Zu hohe Einzahlung. Steuerbetrug? 31340 • --> Hier sind alle Ausnahmen behandelt <-- • Zweite Einzahlung: 3134 • Eingezahlt: 17000, • Abgehoben: 16838, Kontostand: 162 • Eingezahlt: 3134 • Konto ueberzogen beim Abheben von 5758 DM • Konto aufgeloest • --> Hier sind alle Ausnahmen behandelt <-- • Zweite Einzahlung: 31340 • Eingezahlt: 17000, • Abgehoben: 16838, Kontostand: 162 • Eingezahlt: 31340 • Zu hohe Einzahlung. Steuerbetrug? 31340 • --> Hier sind alle Ausnahmen behandelt <--
Passendes catch? • Identifikation der passenden Behandlung? • throw T wird von catch (Klasse W) gefangen, falls gilt: • T ist vom Typ W oder der Typ von T erbt von W • diese catch-Klausel ist die erste Klausel, auf die diese Bedingung paßt.
Beispiel: Definitionen Instanzen dieser Klassen sollen geworfen werden class eins {}; class zwei: public eins {}; class drei { public: void wirfEins() { cout << "\nin wirfEins\n"; throw new eins; } void wirfZwei() { cout << "\nin wirfZwei\n"; throw new zwei; } }; hier wird geworfen
Klasse zwei ist Spezialfall von Klasse eins Benutzung Ausnahme1 void main() { drei * d3 = new drei; try { d3->wirfZwei(); } catch(eins *) { cout << "\nschmeiße eins\n"; } catch(zwei *) { cout << "\nschmeiße zwei\n"; } }
Benutzung Ausnahme1A void main() { drei * d3 = new drei; try { d3->wirfZwei(); } catch(zwei *) { cout << "\nschmeiße zwei\n"; } catch(eins *) { cout << "\nschmeiße eins\n"; } }
Diskussion • Ausgabe: • Ausnahme1: • in wirfZwei • schmeiße eins • Denn: catch(eins *)fängt die Ausnahmethrow (new zwei) (Regel: erste Klausel, die paßt). • Ausnahme1A: • in wirfZwei • schmeiße zwei
Weiterwerfen: class vier { public: drei * three; vier() { three = new drei; } void wirf() { try{ three->wirfEins(); } catch(eins *) { cout << "\n\tgefangen!\n"; throw new zwei; } } }; Ausnahme3 Programm
Was passiert? • Ausgabe: • in wirfEins • gefangen! • schmeiße zwei • In der Klasse vier wird in der Methode wirf durch den Aufruf von three->wirfEins() eine Ausnahme vom Typ eins aktiviert. • Die Ausnahme wird in der Methode wirf behandelt, indem eine Ausnahme von Typ zwei aktiviert wird, die in main behandelt wird.
Klasse vier: Wiederholung class vier { public: drei * three; vier() { three = new drei; } void wirf() { try{ three->wirfEins(); } catch(eins *) { cout << "\n\tgefangen!\n"; throw new zwei; } } };
Klasse vier: exotisch (?) wirft Ausnahme vom Typ eins class vierExotisch { public: drei * three; vierExotisch () { three = new drei; } void wirf() { try{ three->wirfEins(); } catch(zwei *) { cout << "\n\tgefangen!\n"; throw new zwei; } } };
Was passiert? • Die Ausnahme vom Typ eins wird nicht in der Methode wirf, sondern in main behandelt. • Klar: • Es ist kein passender Fänger in der Methode vorhanden.
Für heute: • Ende