Exception, Namespaces

Werbung
02. 02. 2009
Kurs: Programmierung in C/C++
Programmierung in C/C++
Philipp Lucas
[email protected]
02. 02. 2009
Philipp Lucas, CDL, UdS
1
02. 02. 2009
Kurs: Programmierung in C/C++
Heute
◮ Exceptions
◮ Namespaces
Philipp Lucas, CDL, UdS
2
02. 02. 2009
Kurs: Programmierung in C/C++
Exceptions
Ursprung: Abfangen von außergewöhnlichen Fällen (Ausnahmen)
Technisch: Kontrollflußänderung zu dynamisch vorhergehender Stelle
Praktisch: Herausspringen bei Problemen aus tiefer dynamischer Struktur zu zu deren
Behandlung geeigneter Stelle
Besonderheiten:
◮ Ort von try und catch
◮ Wurfobjekte
◮ Interaktion mit anderen Sprachelementen (Konstruktoren, Kontrollfluß,. . . )
◮ Verhalten bei Fehlern (ungefangene Exceptions)
◮ Deklaration von möglichen Würfen
Philipp Lucas, CDL, UdS
3
02. 02. 2009
Kurs: Programmierung in C/C++
Motivation (1)
bool open_file(){
if(!file_open("filename")) return false;
...
}
bool write_data(){
if(!file_open()) return false;
...
}
bool save_state(){
if(!write_data()) return false;
...
}
bool exit_program(){
if(!save_state()) return false;
...
}
int main(){
...
if(!exit_program()){
std::cerr << "An error occured." << std::endl;
}
}
Philipp Lucas, CDL, UdS
4
02. 02. 2009
Motivation
void open_file(){
if(!file_open("filename"))
throw Exception("File open error.");
...
}
void write_data(){
file_open();
...
}
void save_state(){
write_data();
...
}
void exit_program(){
save_state();
...
}
int main(){
...
try{ exit_program(); }
catch(Exception e){ ... }
}
Philipp Lucas, CDL, UdS
Kurs: Programmierung in C/C++
(2)
5
02. 02. 2009
Kurs: Programmierung in C/C++
Kontrollfluss
normal:
_^]\
XYZ[
g
'
Exception:
_^]\
XYZ[
c
PQRS
/ WVUT
f
f
'
g
PQRS
WVUT
g
g
PQRS
WVUT
h
PQRS
/ WVUT
h
Gilt für Funktionen wie auch für Blöcke
Philipp Lucas, CDL, UdS
6
02. 02. 2009
Kurs: Programmierung in C/C++
Einfache Struktur
Werfen:
void print(const Person* const person){
if(person==NULL)
throw Exception("Unspecified argument in print_all.");
...
}
Fangen:
try {
print_header();
print(selection);
print_footer();
} catch (Exception e){
std::cerr << "Error: " << e.error_text();
}
Philipp Lucas, CDL, UdS
7
02. 02. 2009
Kurs: Programmierung in C/C++
Prinzipien
try-catch-Sequenz: Exceptions, die während der Ausführung des try-Blockes geworfen werden, können von der catch-Sequenz behandelt werden.
throw-Ausdruck: Objekt (Argument von throw) wird erzeugt, die Kontrolle geht zum
Anfang des dynamisch nächsthöheren, passenden catch-Blockes.
Auf dem Weg dahin werden die lokalen Variablen zerstört, wie beim normalen Ende
von Blöcken oder Funktionen.
Philipp Lucas, CDL, UdS
8
02. 02. 2009
Kurs: Programmierung in C/C++
Beispiel
try{
func1();
MyClass something;
throw Exception();
func2();
} catch(Exception e){
...
}
Kontrolle geht from throw direkt zum catch-Block:
func2 wird nicht aufgerufen.
Entsprechendes bei Würfen tiefer innerhalb des try-Blockes.
something wird als lokale Variable zerstört, wie beim Ende des Blockes.
Philipp Lucas, CDL, UdS
9
02. 02. 2009
Kurs: Programmierung in C/C++
Wurfobjekte
In C++ kann alles geworfen werden, was kopiert werden kann; es gibt keine dafür
nötige, speziell ausgezeichnete Oberklasse Exception o. ä.
◮ throw new FileAccessException("settings file");
◮ throw FileAccessException("settings file");
◮ throw "Access denied on settings file.\n";
◮ throw 34;
Bibliotheksklassen: Später.
Philipp Lucas, CDL, UdS
10
02. 02. 2009
Kurs: Programmierung in C/C++
Fangen von Exceptions
try { /* ... */
catch(Typ1 o1){
catch(Typ2 o2){
catch(...) { /*
}
/* ... */ }
/* ... */ }
... */ }
◮ Es wird der erste Catch-Block aufgerufen, dessen Parametertyp zur Exception
passt
◮ Geworfenes wird kopiert
◮ Es reicht Angabe des Typs, wenn das Objekt selbst nicht gebraucht wird (wie bei
Funktionen)
◮ ...: Alles wird gefangen
Philipp Lucas, CDL, UdS
11
02. 02. 2009
Kurs: Programmierung in C/C++
Lokale Variablen
Lokale Variablen werden zwischen Wurf und Fangen (stack unwinding) freigegeben:
Destruktoren werden aufgerufen.
Problem: Dynamischer Speicher, sonstige Ressourcen:
try {
int* temp = new int[128];
Aclass aobj();
fill(temp);
do_something(aobj,temp);
delete[] temp;
} catch(...){
std::cerr << "Something bad happened.\n";
}
Wenn fill oder do something eine Exception wirft (und nicht vorher selbst temp
freigibt), dann ist der Speicherplatz, auf den temp zeigt, verloren.
Philipp Lucas, CDL, UdS
12
02. 02. 2009
Kurs: Programmierung in C/C++
Ressourcenmanagement
try {
int* temp = new int[128];
Aclass aobj();
fill(temp);
do_something(aobj,temp);
delete[] temp;
} catch(...){ /* ... */ }
◮ Destruktor der lokalen Variablen aobj wird aufgerufen: aobj kann ihren Speicherplatz aufräumen
◮ temp (der int*) wird auch freigegeben, nicht jedoch der Speicherplatz, auf den
temp zeigt.
◮ Gleiches Problem bei anderen Ressource: Dateihandlern, GUI-Handlern, . . .
◮ Lösung: Ressourcenmanagement in Klassen, deren Objekte als lokale Variablen
deklariert werden.
Philipp Lucas, CDL, UdS
13
02. 02. 2009
Kurs: Programmierung in C/C++
Ressourcenmanagement (2)
try {
std::vector<int> temp(128);
FileHandle handle("log.txt");
fill(temp);
do_something(temp, handle);
} catch(...){ /* ... */ }
◮ temp und handle sind lokale Variablen, die freigegeben werden.
◮ Im Destruktor von std::vector wird der Speicherplatz für das beinhaltete Array
freigegeben.
◮ Im Destruktor von FileHandle schliesst man die geöffnete Datei.
◮ Allgemein: Es werden die Destruktoren aller Objekte aufgerufen, die zwischen Eintritt in den try-Block und dem Auftritt des throw lokal angelegt wurden: Diese
Destruktoren müssen für das Ressourcenmanagement sorgen.
Philipp Lucas, CDL, UdS
14
02. 02. 2009
Kurs: Programmierung in C/C++
Ausnahmen in Konstruktoren
Was passiert, wenn während eines Konstruktors ein Fehler auftritt?
FileHandle("log.txt");: Datei lasse sich nicht öffnen
Fehlermeldung:
◮ Rückgabewert: Geht nicht.
◮ Statusvariable: Geht, Benutzung unelegant.
◮ Exceptions: Ermöglichen die Fehlermeldung auf angemessene Weise.
Philipp Lucas, CDL, UdS
15
02. 02. 2009
Kurs: Programmierung in C/C++
Funktions-Exceptions
Was passiert bei Ausnahmen in der Initialisierungsliste?
unsigned int get_id(){
FileHandle file("log");
file.write("Object created.\n");
file.close();
return ++max_id;
}
A::A() : _id(get_id()) { /* ... */ }
Exception beim Öffnen der Datei wird durch den Konstruktor zu der deklarierenden
Funktion geworfen.
Philipp Lucas, CDL, UdS
16
02. 02. 2009
Kurs: Programmierung in C/C++
Funktions-Exceptions
Behandlung im Konstruktor selbst: Spezialfall der function exception
A::A()
try : _id(get_id())
{ /* ... */ }
catch(FileException){
std::cerr << "Could not open log file.\n";
_id = 0;
}
void f() try { ... } catch (...) { ... }
Falls in Konstruktor/Destruktor:
Gleiche Exception wird automatisch wiedergeworfen.
Philipp Lucas, CDL, UdS
17
02. 02. 2009
Kurs: Programmierung in C/C++
Spezifikation von Exceptions
funcdecl throw(Exceptionlist): Spezifikation aller potentiell herausgeworfenen Exceptions
funcdecl throw(): Zusicherung, daß nichts herausgeworfen wird.
◮ Angabe nur von Exceptions, die herausgeworfen werden:
Was intern behandelt wird, ist egal.
◮ Oberklassen gelten für alle Unterklassen.
◮ Keine Angabe: Alles darf herausgeworfen werden.
◮ Exception-Spezifikation muß bei allen Deklarationen und Definitionen wiederholt
werden.
◮ Virtuelle Funktionen in Unterklassen: Nur Spezialisierung ist erlaubt (Verringerung
der Möglichkeiten).
◮ Konsistenz der Spezifikationen wird zur Compilezeit überprüft; Konsistenz von geworfenen Exceptions mit Spezifikation wird zur Laufzeit überprüft.
Philipp Lucas, CDL, UdS
18
02. 02. 2009
Kurs: Programmierung in C/C++
Beispiel
class Collection {
...
virtual void add_item(const Item*) throw (OutOfMemoryEx, InvalidEx)
// Wirft hoechstens diese beiden Exceptions oder Untertypen davon.
virtual unsigned int size() const throw();
// Wirft gar keine Exception.
};
class SpecialCollection : Collection{
...
virtual void add_item(const Item*) throw (OutOfMemoryEx) ;
// Weniger als in Collection: Darf.
virtual unsigned int size() const;
// Mehr als in Collection: Darf nicht.
};
Philipp Lucas, CDL, UdS
19
02. 02. 2009
Kurs: Programmierung in C/C++
Wiederwerfen
throw; ohne Argument: Gefangene Exception wiederwerfen.
void g(){ throw "Exception."; }
void f(){
int* a;
try{
a = new int[21];
g(a);
delete[] a;
} catch(...){
delete[] a;
throw;
}
}
Philipp Lucas, CDL, UdS
20
02. 02. 2009
Kurs: Programmierung in C/C++
Exceptions in der Standardbibliothek
exception
iSSSS
SSSS
SSSS
SSS
k5
kkk
kkk
k
k
kk
kkk
logic error
runtime errors
KS
KS
length error/domain error/
out of range/invalid argument
range error/
overflow error/underflow error
exception
eeee2
eeeeee jjjjj5
e
e
e
e
e
jjj
eeeeee
jjjj
eeeeee
e
j
e
j
e
e
j
e
eeeeee
bad alloc
bad exception
O
iSSkXSXXXXXXXXX
SSS
SSS XXXXXXXXXX
SSS
XXXXX
SS
XXXXX
XXX
ios base::failure
bad typeid
bad cast
Allgemeines Rahmenwerk zur Organisation von Exceptions.
Philipp Lucas, CDL, UdS
21
02. 02. 2009
Kurs: Programmierung in C/C++
Bibliotheks-Exception
#include <exception>
class exception{
public:
...
virtual const char* what() const;
};
Alle Standardausnahmen erben von exception.
Philipp Lucas, CDL, UdS
22
02. 02. 2009
Kurs: Programmierung in C/C++
Wichtige Bibliotheks-Exceptions
◮ std::vector::at() und std::string::at() werfen out of range
Geprüfte Varianten von operator[]().
◮ new wirft bad alloc, wenn nicht genügend Speicherplatz da ist.
Dies kommt teilweise heraus, z. B. bei STL-Containern.
◮ ios wirft ios base::failure bei spezifizierten Übergängen nach bad(),
eof() oder fail()
std::cout.exceptions(ios base::badbit | ios base::failbit)
Philipp Lucas, CDL, UdS
23
02. 02. 2009
Kurs: Programmierung in C/C++
Verhalten bei dynamischen Problemen
Hier nur zwei Beispiele:
◮ Funktion wirft Ausnahme, die nicht in der deklarierten Ausnahmeliste steht:
std::unexpected() wird aufgerufen; deren Verhalten kann spezifiziert werden
(set unexpected(handler function))
◮ Ausnahme wird nicht gefangen:
std::terminate() wird aufgerufen; deren Verhalten kann spezifiziert werden
(set terminate(handler function))
Philipp Lucas, CDL, UdS
24
02. 02. 2009
Kurs: Programmierung in C/C++
Sonderfälle
Sonderfälle wie:
◮ Exception im Destruktor einer automatischen Variablen
◮ Exception im Destruktor einer static Variablen nach dem Ende der mainFunktion
◮ Exception im Copy-Konstruktor der temporären Variable während des Fangens der
Exception
◮ std::bad exception-Exception im Handler für undeklariert geworfenen Exceptions
→ nicht in dieser Vorlesung, aber wichtig beim Behandeln von Exceptions in größeren
Projekten.
Philipp Lucas, CDL, UdS
25
02. 02. 2009
Kurs: Programmierung in C/C++
Weiteres
◮ Exceptions sind ein Mechanismus für den internen Programmablauf: Es sind keine
Signale.
◮ Exceptions müssen keine Fehler signalisieren: Man kann Exceptions als nichtlokale return-Anweisungen oder tiefere break ansehen (Ausbruch aus geschachtelter Schleife).
◮ Bei Exceptions, die Fehler signalisieren, kann man nicht in allen Fällen beim Fangen den Fehler beheben: Programmabbruch, aber gesteuert.
Philipp Lucas, CDL, UdS
26
02. 02. 2009
Kurs: Programmierung in C/C++
Namespaces
Namespace: Optionale benannte Region
◮ Zugriff auf Namespace-Mitglieder wie bei Klassen mit ::
◮ Sinn: Auseinanderhalten von separatem Code, keine Nameclashes, Interfaces
◮ Können geschachtelt werden
◮ Import-ähnliche Direktiven
Bekannter Namespace: std
Allgemein: Achtung bei älteren Compiler-Versionen.
Philipp Lucas, CDL, UdS
27
02. 02. 2009
Kurs: Programmierung in C/C++
Namespaces
namespace GUI {
class Window{
public:
Window(const unsigned int size_x, const unsigned int size_y);
enum vis_t { NO, WINDOW, FULL };
void set_visibility(const vis_t vis);
...
};
const char version[];
}
GUI::Window app_win(600,400);
window.set_visibility(GUI::Window::WINDOW);
std::cout << "Using version " << GUI::version << ’.’ << std::endl;
Philipp Lucas, CDL, UdS
28
02. 02. 2009
Kurs: Programmierung in C/C++
Namespaces (2)
Schachtelung, globaler Namespace:
namespace A{
int f(int);
namespace B{
void g(int){
f(6);
}
}
}
void f(int);
namespace B {
void f(int);
void g(){
A::B::g(A::f(7));
f(8);
::f(12);
}
}
Philipp Lucas, CDL, UdS
29
02. 02. 2009
Kurs: Programmierung in C/C++
Namespace-Erweiterungen
◮ Namespaces können geschachtelt werden (siehe Beispiel)
◮ Namespace-Aliases:
namespace Lib = VendorLibrary v3 2a;
◮ Namespaces können erweitert werden (im Gegensatz zu Klassen).
Technik:
⊲ Header: Namespace-Deklarationen mit Interface-Typen, -Funktionen etc.
⊲ Implementierung: Namespace-Deklaration erweitert mit für die Implementierung
nötigen Klassen etc.
std darf i. W. nicht erweitert werden.
Philipp Lucas, CDL, UdS
30
02. 02. 2009
Kurs: Programmierung in C/C++
Deklaration/Definition
namespace A{
void f();
// Deklaration
}
void A::f() { } // Definition
namespace B{
void f(); // Deklaration
}
namespace B{
void ff() { } // Definition einer anderen Funktion
}
Philipp Lucas, CDL, UdS
31
02. 02. 2009
Kurs: Programmierung in C/C++
using-Deklarationen
Import bestimmter Bezeichner (using-Deklarationen):
using qualifizierte ID;
Bezeichner ist nun ohne Qualifikation innerhalb des aktuellen Scopes sichtbar.
Beispiel:
using std::cout;
using std::endl;
cout << std::width(6) << 45 << endl;
Achtung:
namespace A { enum Colour { Green, Yellow, Red }; }
using A::Colour;
Colour ist importiert, nicht jedoch Green etc.
Philipp Lucas, CDL, UdS
32
02. 02. 2009
Kurs: Programmierung in C/C++
using-Direktiven
Import aller zur Verfügung stehenden Bezeichner (using-Direktiven):
using namespace NS;
Die Bezeichner in NS sind nun ohne Qualifikation innerhalb des aktuellen Scopes
sichtbar.
Beispiel:
using namespace std;
cout << width(6) << 45 << endl;
Es können zusätzliche Namenskollisionen auftauchen:
void f(const char* const string){
string var = string;
}
Philipp Lucas, CDL, UdS
33
02. 02. 2009
Kurs: Programmierung in C/C++
using-Spezifikationen
Sichbarkeitsregeln für using-Spezifikationen (Direktiven oder Deklarationen):
◮ Überladene Funktionen werden in allen Varianten durch Deklarationen importiert.
◮ Aber: Sichtbarkeit zum Zeitpunkt von using:
namespace A {
using A::f;
namespace A {
...
f(1.0);
//
A::f(1.0); //
void f(int); }
void f(double); }
Ruft int-Funktion auf.
Ruft double-Funktion auf.
◮ Doppeldeklaration durch using-Direktive: Nur wo erlaubt
Philipp Lucas, CDL, UdS
34
02. 02. 2009
Kurs: Programmierung in C/C++
using-Spezifikationen (2)
◮ Explizite Deklarationen (normal oder mit using) sind sichtbarer als durch Direktiven importierte Namen.
◮ Schachtelung: Sichtbarkeit wird herausgeführt (transitiv):
namespace A { void f() {} }
namespace B { using namespace A;
using namespace B;
...
g();
B::f();
f();
void g() {} }
◮ using von Methoden: Hier nicht.
Philipp Lucas, CDL, UdS
35
02. 02. 2009
Kurs: Programmierung in C/C++
Argument dependent lookup
Argument-dependent name lookup:
Idee: Wenn Argumente einer Funktion in einem bestimmten Namespace deklariert
sind, so sollte man die Funktion auch dort suchen.
namespace A{
enum enum_t { E };
void f(int) { }
void f(enum_t) { }
}
int main(){
const A::enum_t e = A::E;
A::f(7);
f(e);
}
Philipp Lucas, CDL, UdS
36
02. 02. 2009
Kurs: Programmierung in C/C++
Unnamed Namespaces
namespace {
void f() {}
}
Unbenannter Namensraum: Deklaration äquivalent zu
namespace EINDEUTIGE_ID{
void f() {}
}
using EINDEUTIGE_ID;
Effekt:
◮ benutzbar innerhalb der Datei, aber nicht außerhalb
◮ C++-Alternative zu static
Philipp Lucas, CDL, UdS
37
02. 02. 2009
Kurs: Programmierung in C/C++
Ausblick
◮ Diverses
Philipp Lucas, CDL, UdS
38
Herunterladen