Buchlisting über für exceptions fehlerhaft
-
Guten Tag, ich versuche mir gerade das Umgehen mit exceptions in C++ beizubringen. leider ihn Erfolg. Da ich herausgefunden habe, dass das Beispiel in meinem Buch fehlerhaft ist, würde ich gern wissen warum.
Wenn man '0' eingibt, wird nur merkwürdiger Textsalat ausgegeben.Wozu definiert er überhaupt das Element string s in KeineDatenMehr()?
Warum gibt er einen Pointer auf eine lokale Variable zurück?Ich benutze den Visual C++ Compiler 2010. Kann mir jemand erklären, wie das funktionieren soll?
Der Ausschnitt ist aus dem Buch Einstieg in C++ von galileo computing.
-
Heute will echt nichts funktionieren... Sorry, dass ich das Listing vergessen habe -.-
#include <iostream> #include <exception> #include <string> #include <sstream> using namespace std; // Meine eigene Basisklasse, abgeleitet von exception class meaCulpa : public exception { public: meaCulpa(string s) {this->s = s;} virtual ~meaCulpa() throw() {} virtual const char * what() const throw() {return s.c_str();} private: string s; }; // Besonderer Fehler, wenn keine Daten mehr vorliegen class KeineDatenMehr : public meaCulpa { public: KeineDatenMehr(int a) : meaCulpa(" ") {nr = a;} virtual ~KeineDatenMehr() throw() {} virtual const char * what() const throw(); private: int nr; string s; }; // what() wird für eigene Fehlermeldung überschrieben const char * KeineDatenMehr::what() const throw() { ostringstream getNr; getNr << "Keine Daten mehr. Fehlernr.: " << nr; return getNr.str().c_str(); } // Eine weitere Fehlerart wird von meaCulpa abgeleitet. class QuelleFehlt : public meaCulpa { public: QuelleFehlt() : meaCulpa("Quelle fehlt") {} }; // Tuwas simuliert die beiden Fehlerarten in Abhängigkeit vom // Parameter void Tuwas(int Problem) throw (KeineDatenMehr, QuelleFehlt) { if (Problem==0) { throw KeineDatenMehr(8); } if (Problem==1) { throw QuelleFehlt(); } } int main() { // Problem-Nummer eingeben int Auswahl; cout << "Zahl zwischen 0 und 3 eingeben:" << endl; cin >> Auswahl; // Der try-Block fängt die Exception in Tuwas try { Tuwas(Auswahl); } // Fängt nur die eigenen Fehler catch(meaCulpa& fehler) { cout << fehler.what() << endl; } cin.ignore(std::numeric_limits<streamsize>::max(), '\n'); cin.get(); }
-
Typischer Anfängerfehler, wie nicht anders zu erwarten beim Galileoverlag:
const char * KeineDatenMehr::what() const throw() { ostringstream getNr; getNr << "Keine Daten mehr. Fehlernr.: " << nr; return getNr.str().c_str(); }Hier gibst du einen Verweis auf ein lokales Objekt zurück. Dieses existiert aber nicht mehr, nachdem die Funktion verlassen wurde.
Wie immer gilt: Finger weg vom Galileoverlag! Siehe zweiter Link in meiner Signatur.
P.S.: Exceptionspezifikationen an Funktionen gelten als "evil" und sind im neuen Standard auf veraltet gesetzt worden. Sehr gut oder aktuell ist das Thema des Buches also auch nicht.
-
Der Code ist grauenhaft. Du hast ja schon die Fehler erkannt. Er gibt einen Pointer auf eine lokale Variable zurück und deshalb erhältst du bei der Ausgabe Textsalat. Vielleicht wollte er den String nach s kopieren. Ansonsten ergibt s keinen Sinn in KeineDatenMehr. Und Exceptionspecifications (Z.48) sollte man einfach nicht benutzen. Wurden die in C++11 nicht eh deprecated?
Wenn der Code aus einem Buch stammt, dann würde ich dir ganz klar empfehlen ein andere Buch zu benutzen. zB http://stroustrup.com/Programming/
-
Ist das wieder JW?
-
Bashar schrieb:
Ist das wieder JW?
Nein, Willemer. Anscheinend auch nicht besser
-
Danke euch! Ich habe es mittlerweile geschafft, den Quelltext zu korrigieren. Allerdings verstehe ich nicht, warum diese Version nicht funktioniert.
Ich habe schliesslich das Stringstreamobjekt innerhalb der Klasse deklariert, sodass es bei der Rückkehr der Funktion nicht gelöscht werden sollte.Folgenden Fehler bekomme ich von meinem Compiler:
1>d:\programme\microsoft visual studio 10.0\vc\include\sstream(724): error C2248: "std::basic_ios<_Elem,_Traits>::basic_ios": Kein Zugriff auf private Member, dessen Deklaration in der std::basic_ios<_Elem,_Traits>-Klasse erfolgte.
#include <iostream> #include <exception> #include <string> #include <sstream> using namespace std; // Meine eigene Basisklasse, abgeleitet von exception class meaCulpa : public exception { public: meaCulpa(string s) {this->s = s;} virtual ~meaCulpa() {} virtual const char * what() {return s.c_str();} private: string s; }; // Besonderer Fehler, wenn keine Daten mehr vorliegen class KeineDatenMehr : public meaCulpa { public: KeineDatenMehr(int a) : meaCulpa(" ") {nr = a;} virtual ~KeineDatenMehr(){} virtual const char * what(); private: int nr; stringstream myStream; //stringstream ist jetzt innerhalb der Klasse, string s gelöscht }; // what() wird für eigene Fehlermeldung überschrieben const char * KeineDatenMehr::what() { myStream << "Keine Daten mehr. Fehlernr.: " << nr; return myStream.str().c_str(); } // Eine weitere Fehlerart wird von meaCulpa abgeleitet. class QuelleFehlt : public meaCulpa { public: QuelleFehlt() : meaCulpa("Quelle fehlt") {} }; // Tuwas simuliert die beiden Fehlerarten in Abhängigkeit vom // Parameter void Tuwas(int Problem) { if (Problem==0) { throw KeineDatenMehr(8); } if (Problem==1) { throw QuelleFehlt(); } } int main() { // Problem-Nummer eingeben int Auswahl; cout << "Zahl zwischen 0 und 3 eingeben:" << endl; cin >> Auswahl; // Der try-Block fängt die Exception in Tuwas try { Tuwas(Auswahl); } // Fängt nur die eigenen Fehler catch(meaCulpa& fehler) { cout << fehler.what() << endl; } cin.ignore(std::numeric_limits<streamsize>::max(), '\n'); cin.get(); }
-
Da dürfte es um den Kopierkonstruktor gehen. Die Streams sind generell nicht kopierbar. Wenn jetzt der Compiler für deine Klasse KeineDatenMehr einen Kopierkonstruktor generiert, ruft er dort drin die Kopierkonstruktoren aller Membervariablen auf ... aber stringstream besitzt keinen bzw. lediglich einen, der private ist.
Ich seh auch nicht ganz, wozu das gut sein soll. Speicher dir doch den String und nicht den Stream...
-
Ok, ich dachte ich könnte damit den Speicherplatz für den String sparen, aber das ist eigentlich irrelevant, weil der Stringstream eh nach der Ausführung der Funktion gelöscht wird...
-
Und der Stream belegt wohl keinen Speicher?

Ich würde mal vorschlagen: Wegwerfen und neumachen. Es sei denn du hast Spaß daran das verkorkste Vorbild aus dem Buch glattzuziehen.
-
Ich würde mal vorschlagen: Wegwerfen und neumachen. Es sei denn du hast Spaß daran das verkorkste Vorbild aus dem Buch glattzuziehen.
Da hast du wohl oder übel recht

Und der Stream belegt wohl keinen Speicher?

Den Stream brauche ich ja so oder so zum Erstellen des strings für what(). Den String würde ich theoretisch nicht benötigen, wenn ich den String innerhalb der Funktion über sstream.str().c_str() zurückgeben könnte.
-
Gib doch bei what() einfach einen String zurück, anstatt einem char*.
-
Buchleser schrieb:
Und der Stream belegt wohl keinen Speicher?

Den Stream brauche ich ja so oder so zum Erstellen des strings für what().
Aber nur lokal. Ich dachte es ginge dir um den Speicher, den das Exception-Objekt benötigt.
Mir fällt übrigens grade ein, dass es auch nicht gehen würde, wenn stringstream kopierbar wäre.
Bei
return sstream.str().c_str();wird ja ein temporärer
stringerzeugt und auf diesenc_str()aufgerufen. Derstringexistiert damit außerhalb vonwhat()nicht mehr, so dass derchar-Zeiger ungültig ist.SeppJ schrieb:
Gib doch bei what() einfach einen String zurück, anstatt einem char*.
Er hält sich an die Vorgabe von
std::exception. Würdest du deine eigene Exception-Hierarchie nebenher bauen, um einwhatmitstring-Rückgabe zu haben?
-
class KeineDatenMehr : public meaCulpa { public: KeineDatenMehr(int nr) : meaCulpa("Fehler nummer: " + std::to_string(nr)) { } virtual ~KeineDatenMehr() throw() {} };