Twitter und Facebook-Anbindung
X
Tweet Follow @twitterapi
!!! Anbindung an twitter und facebook öffnen !!!

Wenn Ihnen mein Online-Buch gefällt,
dann bedanken Sie sich doch mit einer kleinen Spende...

26.4 Exceptions weiterleiten

26.4 Exceptions weiterleiten

Hin und wieder kommt es vor, dass eine Exception an einer Stelle ausgelöst wird, an welcher man sie überhaupt nicht gebrauchen kann, da sie in der fünften Unterfunktion ausgelöst wird und man in den dazwischen liegenden Funktionen nicht darauf reagieren will bzw. sogar garnicht kann. Dies klingt jetzt ein wenig verwirrend, aber ich werde dies an einem keinen Beispiel klar machen.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
					
// Wirft eine Exception ///////////////////////////////////////////////////////
int Unterfunktion2() {
	throw "Ich bin ein Fehler an einer ungluecklichen Stelle!";
	return 0;
} // Unterfunktion2 ///////////////////////////////////////////////////////////



// Ruft eine Funktion auf, welche eine Exception wirft ////////////////////////
void Unterfunktion1() {
	char*	pstrText	= new char[80];
	printf("Speicher fuer Text wurde reserviert\n");

	int	iErgebnis	= Unterfunktion2();

	delete pstrText;
	printf("Speicher fuer Text wurde wieder freigegeben\n");

	printf("Das Ergebnis der Funktion ist %i\n", iErgebnis);
} // Unterfunktion1 ///////////////////////////////////////////////////////////


// Hauptfunktion der Anwendung ////////////////////////////////////////////////
int main(int argc, char** argv) {
	try {
		Unterfunktion1();
	} catch (const char* pcstrMessage) {
		printf("Fehler: %s\n", pcstrMessage);
	} // end of try

	return 0;
} // main /////////////////////////////////////////////////////////////////////
					

Ausgabe:

Speicher fuer Text wurde reserviert
Ich bin ein Fehler an einer ungluecklichen Stelle!
		

Allein an der Ausgabe sollten Sie jetzt erkennen, dass etwas nicht so läuft, wie gewünscht, aber warum ist dies so? In dem Moment, in welchem eine Exception ausgelöst wird, werden alle Funktionen sofort beendet, welche nicht die Ausnahme abfangen. Für mein Beispiel bedeutet dies, dass die Funktion "Unterfunktion1" auch beendet wird, da ich hier die Exception nicht abfange. Dies hat zur Folge, dass die Funktion nicht mehr dazu kommt, den reservierten Speicher wieder freizugeben und ich erzeuge ein Speicherleck. Nun könnte ich diese Funktion auch folgendermaßen abändern.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
					
// Ruft eine Funktion auf, welche eine Exception wirft ////////////////////////
void Unterfunktion1() {
	char*		pstrText	= new char[80];
	printf("Speicher fuer Text wurde reserviert\n");

	try {
		int	iErgebnis	= Unterfunktion2();
	} catch (const char astrMessage) {
		printf("Fehler: %s\n", astrMessage);
	} // end of try

	delete pstrText;
	printf("Speicher fuer Text wurde wieder freigegeben\n");

	printf("Das Ergebnis der Funktion ist %i\n", iErgebnis);
} // Unterfunktion1 ///////////////////////////////////////////////////////////
					

Mal abgesehen davon, dass ich jetzt in Zeile 15 nicht mehr auf die Variable "iErgebnis" zugreifen könnte, da sie innerhalb des "try" Blockes definiert wurde und ihr Wert zudem unbestimmt wäre, da die Funktion "Unterfunktion2" nicht dazu kommt, einen Wert zurückzugeben, wäre mein Problem damit nicht gänzlich gelöst, da die Funktion "Unterfunktion1" normal weiter läuft, obwohl wichtige Informationen fehlen, die zur Weiterverarbeitung wichtig sein könnten. Zudem bekommt die Hauptfunktion nicht mit, dass irgendwo etwas schief gelaufen ist. Sinnvoller wäre es also, auch die Funktion "Unterfunktion1" zu beenden bzw. sogar die Ausnahme weiter zu reichen. Dies sähe z.B. so aus.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
					
// Ruft eine Funktion auf, welche eine Exception wirft ////////////////////////
void Unterfunktion1() {
	char*		pstrText	= new char[80];
	printf("Speicher fuer Text wurde reserviert\n");

	try {
		int	iErgebnis	= Unterfunktion2();
		printf("Das Ergebnis der Funktion ist %i\n", iErgebnis);
	} catch (const char astrMessage) {
		printf("Fehler: %s\n", astrMessage);
		delete pstrText;
		printf("Speicher fuer Text wurde wieder freigegeben\n");

		throw astrMessage;
	} // end of try
} // Unterfunktion1 ///////////////////////////////////////////////////////////
					

In Zeile 14 sehen Sie jetzt, dass ich aufgefangene Exception an die "main" weiterleite. Dieses Vorgehen wäre durchaus denkbar, aber hat trotzdem noch seine Schwachstellen. Stellen Sie sich vor, dass Sie jetzt mehrere Funktionen aufrufen und auch mehrere unterschiedliche Exceptions auftreten könnten, von denen Sie sogar im schlimmsten Falle nicht den Typ wissen. Sicher denken Sie jetzt, dass Sie ja mehrere "catch", Blöcke definieren könnten und falls ein Fehlertyp auftritt, den Sie nicht kennen, könnten Sie ja die Sache mit den drei Punkten machen und in diesem Fall eine neue Ausnahme erzeugen. Aber damit zerstören Sie nicht nur Informationen über die eigentliche Fehlerursache, sondern haben zudem viel Schreibaufwand (besonders, wenn es noch vier, fünf andere Aufrufebenen gibt). Aus diesem Grund gibt es einen s.g. "re-throw" Mechanismus, welcher die aufgefangene Exception ungesehen weiter leitet. Dies sieht dann wie folgt aus.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
					
// Ruft eine Funktion auf, welche eine Exception wirft ////////////////////////
void Unterfunktion1() {
	char*		pstrText	= new char[80];
	printf("Speicher fuer Text wurde reserviert\n");

	try {
		int	iErgebnis	= Unterfunktion2();
		printf("Das Ergebnis der Funktion ist %i\n", iErgebnis);
	} catch (...) {
		delete pstrText;
		printf("Speicher fuer Text wurde wieder freigegeben\n");

		throw;		// Nur innerhalb des catch Blockes möglich
	} // end of try
} // Unterfunktion1 ///////////////////////////////////////////////////////////
					

Ausgabe:

Speicher fuer Text wurde reserviert
Speicher fuer Text wurde wieder freigegeben
Ich bin ein Fehler an einer ungluecklichen Stelle!
		

Sie sehen also in Zeile 9, dass es gar nicht nötig ist, auf alle möglichen Exceptions zu reagieren und in Zeile 13 sehen Sie zudem, dass ein einfaches aufrufen von "throw" reicht, um die gefangene Ausnahme weiterzuleiten. Zudem konnte ich die Ausgabe der Fehlermeldung wieder löschen und kann die "main" sich darum kümmern lassen. Wie der Kommentar schon verrät, darf man diese Art der Ausnahmeauslösung nur innerhalb des "catch" Blockes tätigen. Außerhalb benötigt das "throw" immer noch einen Wert (egal was für einen und von welchem Typ). Sie sehen zudem anhand der Ausgabe, dass jetzt der Speicher für den String, ordnungsgemäß freigegeben wird. Leider müssen Sie dieses "re-throw" in alle Aufrufebenen einbauen, in welchen Sie nicht konkret auf die Exception eingehen wollen, allerdings hält sich der Aufwand noch in Grenzen, da man wie gesagt, nicht auf alle erdenklichen Fälle reagieren muss.

Abschließend möchte ich Sie noch auf die vordefinierten Exceptionklassen der Standardbibliothek hinweisen, welche zum einen von Standardfunktionen gefeuert werden und zum anderen überschrieben werden können. Beispielsweise finden Sie im Header "exception" die Klassen "bad_alloc" und "bad_exception". Im Header "stdexcept" gibt es u.a. die Klassen "invalid_argument", "length_error", "overflow_error" und "range_error". Zudem gibt es noch die Header "typeinfo" und "ios", in welchen weitere Exceptionklassen definiert wurden. Ich möchte allerdings an dieser Stelle nicht im einzelnen auf diese Klassen eingehen und verweise Sie diesbezüglich auf das MSDN, in welchen Sie mehr Informationen über dieses Thema und bereitstehende Klassen, erhalten.

Zum Seitenanfang
Zum Inhaltsverzeichnis

© Copyright by Thomas Weiß, 2009 - 2012