Index Referenz
Richtlinien Bemerkungen
Bemerkungen
Hier einige Bemerkungen zu g++, Ausnahmebehandlung, STL, Template-Klassen
etc.
STL, Namespaces und der GNU Compiler g++
Header-Dateien
Die Header-Dateien werden in der C++-Standardbibliothek ohne irgendeine
Endung eingebunden:
#include <string> // die Klasse string aus der STL
#include <vector> // die Klasse vector aus der STL
Bei C-Header-Dateien wird dabei noch ein "c" im Namen vorangestellt:
#include <cstring> // die klassischen C-String-Funktionen
#include <cmath> // die alten mathematischen Funktionen
So ist es möglich, zwischen den beiden zu unterscheiden. Dem GNU
Compiler fehlen noch einige der neuen Header-Dateien, z.B. diejenigen
für I/O. In solchen Fällen muß man
#include <iostream.h>
#include <fstream.h>
benutzen. In der Praxis hat sich herausgestellt, daß die STL weniger
Probleme bereitet, wenn sich der Entwickler an die neuen Regeln hält.
Namensbereiche
Alle Identifier in der C++-Standardbibliothek wurden im Namensbereich std
definiert. Um sie "wie gewohnt" zu benutzen, muß hinter der letzten
Include-Anweisung
stehen. Der GNU Compiler unterstützt zur Zeit keine Namensbereiche
- jede using-Anweisung bewirkt eine Warnung. Die Anweisung wird
aber akzeptiert. Damit aulib++ auch mit einem anderen Compiler problemlos
übersetzt werden kann, sollte man using benutzen.
g++ und die Ausnahmebehandlung
Ausnahmebehandlung und Vererbung
Normalerweise werden in einer Bibliothek mehrere Exception-Klassen definiert.
Sie bilden eine Vererbungshierarchie der Art:
error -> logic_error -> invalid_argument
|
-> out_of_range
|
-> length_error
|
-> runtime_error -> overflow
-> underflow
-> io_error
So ist es möglich, je nach Art des Fehlers, unterschiedlich zu reagieren:
try {
...
throw runtime_error();
...
throw logic_error();
}
catch(const runtime_error& e) {
// breche ab mit einer Fehlermeldung
}
catch(const logic_error& e) {
// fordere den Benutzer auf, die Eingabe zu korrigieren
}
aber auch ganz einfach Fehler abzufangen:
try {
...
throw runtime_error();
...
throw logic_error();
}
catch(const error& e) {
// breche ab mit einer Fehlermeldung
}
Der GNU Compiler unterstützt dies leider nicht. Der Typ der
mit throw erzeugten Ausnahme muß mit dem in catch()
spezifizierten Typen exakt übereinstimmen. In aulib++ wird deshalb
nur eine Klasse Exception verwendet.
Ausnahmen und Funktionsdeklarationen
Die Deklaration einer Funktion f, die eine Ausnahme vom Typ Exception
auslösen kann, sieht wie folgt aus:
void f() throw(Exception) const;
Der throw-Ausdruck legt fest, welche Ausnahmen von f ausgelöst
werden können. Der GNU Compiler betrachtet solche Deklarationen
als Fehler. In der aulib++ existiert deshalb das Makro _throw_exception_,
und die Deklaration von f würde so aussehen:
void f() _throw_exception_ const;
Wird die Bibliothek mit g++ übersetzt, dann ersetzt der Precompiler
_throw_exception_ durch eine leere Zeichenkette. Wird ein anderer
Compiler benutzt, so kann man bei der Übersetzung _throw_exception_
explizit auf throw(Exception) setzen.
Template-Klassen und Typedefs
Wer Template-Klassen benutzt, kann sich das Leben etwas einfacher machen,
indem er typedefs einsetzt. Anstatt zu schreiben:
kann man zuerst ein typedef hinschreiben:
und dann die Klasse wie eine ganz normale Klasse verwenden:
IntVector v;
...
v = new IntVector(20);
...
void f(IntVector v) { ... }
Auf die Art und Weise ist z.B. string in der STL deklariert. Wer
merkt es schon, dass es sich um ein typedef und nicht um eine echte Klasse
handelt?
Testen von Programmen, Festhalten der Zwischenergebnisse
Nicht jeder sucht nach Fehlern mit Hilfe von gdb (der GNU-Debugger). Beim
Testen eines Programms ohne Debugger wird der Programmcode meistens erweitert:
hier die Ausgabe eines Zwischenergebnisses, da eine Meldung, dass die Funktion
gestartet wurde usw. Sind die Fehler beseitigt, muss man all die Ausgabeoperatoren
wieder entfernen. Mit Hilfe eines Makros kann man sich die Arbeit ersparen:
#ifdef TEST
// hier folgt die Ausgabe der Zwischenergebnisse
#endif
Wird beim Uebersetzen des Programms das Makro TEST definiert (g++ -DTEST),
so werden die Ausgabeoperationen beruecksichtigt. Ist das Makro nicht definiert,
dann laesst der Praeprozessor die Zeilen weg. Eins ist allerdings zu beachten:
der Code sollte lesbar sein! Die ifdefs sind deshalb am besten
am Anfang bzw. am Ende einer Funktion untergebracht.
In der aulib++ ist fuer die Ausgabe von Zwischenergebnissen das ostream-Objekt
cres vorgesehen. Wird bei der Uebersetzung des Programms das Makro
LOGFILE (g++ -DLOGFILE) definiert, so werden alle Zwischenergebnisse in
die Datei results.log geschrieben, andernfalls ist cres
identisch mit cerr. Eine Funktion f koennte nun wie folgt aussehen:
int f(double a, double b) {
#ifdef TEST
cres << "f(double a = " << a << ", double b =
" << b << ")" << endl;
#endif
int c;
// hier folgt der Funktionsrumpf, in dem der Wert c berechnet wird
#ifdef TEST
cres << "return = " << c << endl;
#endif
}
Last modified $Id: remarks.htm,v 1.1 1998/01/27 14:42:14
tomczyk Exp tomczyk $