Exception implementierung: Speicherzugriffsfehler
-
Hi, zurzeit versuche ich eine exception-klasse zu schreiben, die einen fehler string übergeben bekommt und diesen ausgeben kann, nebenbei sollen die implementierung-details der klasse versteckt werden. Biserher habe ich das so realisiert:
class exception { public: exception(const std::string& error) throw(); ~exception() throw(); const std::string& what() const throw(); private: class exception_impl* my; }; class exception_impl { public: const std::string error; exception_impl(const std::string& err) : error(err) { } ~exception_impl() { } }; exception::exception(const std::string& error) throw() : my(new exception_impl(error)) { } exception::~exception() throw() { delete my; } const std::string& exception::what() const throw() { return my->error; }Der Aufruf erfoglt dann so:
int main(int argc, char* argv[]) { try { throw exception("exceptional"); } catch(exception e) { std::cerr << e.what() << std::endl; } return 0; }Doch dabei erhalte ich dann folgende ausgabe:
exceptional SpeicherzugriffsfehlerDie Klasse funktioniert also wie gewünscht, bis auf der Speicherzugriffsfehler. Soweit ich herausfinden konnte hängt dieser mit dem "delete my;" in zeile 27 der exception-implementation zusammen, mir is jedoch nicht bewusst wie ich dies umgehen könnte, ohne ein speicherleck zu provozieren.
Vielen Dank für euere Hilfe!
-
Dir fehlt wohl der Kopierkonstruktor (Zuweisungsoperator wäre auch nicht schlecht).
Wird die exception zerstört wird immer das my ebenfalls zerstört. du holst dir in dem catch nur ne Kopie, ich weiß nicht inwieweit bei dem throw schon kopiert wird. Jedenfalls sorgt der fehlende Kopierkonstruktor dafür dass nur der Pointer kopiert wird. Wird der zerstört versucht die Kopie weiterhin darauf zuzugreifen -> Segfault.
-
Du hast keinen Kopierkonstruktor und Zuweisungsoperator definiert. Das Objekt wird kopiert, und jedes Mal zerstört, wobei
deletemehrfach auf den selben Speicherbereich angewendet wird.Verzichte auf
newund benutze Wertsemantik.exception_impl my;Benötigt zwar die Definition von
exception_impl, aber bei der Grösse ist das kein Problem. Du könntest auf diese Klasse ohnehin verzichten.
-
noch am rande:
exception::exception(const std::string& error) throw() : my(new exception_impl(error)) { }pfui! wenn ne exception fliegen kann(und das kann sie definitiv, weil dort 2 news sind), dann schreib das throw() doch einfach nicht hin...
bb
-
Nochmal eine Nachfrage...
Da ich meine Copy-Constructor und den operator== immer vorbildlich implementiere ^^,
ist mir folgendes noch nie aufgefallen.Ich meine in Erinnerung zu haben, dass man mich in meinen C++-Anfängen aufgeklärt
hat, dass man (in der Regel) Objekte schmeißt, statt Zeiger darauf.
Da war ich etwas verwundert, dass man sie als Referenz fängt, da diese nach
meiner naiven Annahme ja auf dem Stack liegen. Jedoch hat man mir darauf gesagt,
dass diese an einen sicheren Ort kopiert werden ohne (!!!) jedoch den Copy-
Construktor aufzurufen.Jetzt habe ich gerade den Code kopiert.
Ursprüngliche Ausgabe:
exceptional test(2819) malloc: *** error for object 0x100150: double free *** set a breakpoint in malloc_error_break to debug test(2819) malloc: *** error for object 0x1001e0: double free *** set a breakpoint in malloc_error_break to debugZwei Speicherzugriffsfehler, weil ja auch der interne string doppelt freigegeben wird.
Das catch habe ich dann mit einer Referenz ersetzt:
try { throw exception("exceptional"); } catch(const exception& e) { std::cerr << e.what() << std::endl; }Ausgabe:
exceptionalDann kriege ich keinen Speicherzugriffsfehler.
Wenn ich jetzt eine Ausgabe im Copy-Constructor mache, dann wird sie nicht ausgegeben.exception::exception(const exception& e){ std::cout << "copy" << std::endl; }exceptionalWenn ich jedoch den Copy-Constructor private mache, kompiliert das Programm nicht mehr.
private: exception(const exception& e);test.cpp:26: error: ‘exception::exception(const exception&)’ is private test.cpp:44: error: within this context test.cpp:26: error: ‘exception::exception(const exception&)’ is private test.cpp:44: error: within this contextDer Vollständigkeit wegen:
Wenn ich in den ursprünglichen Code die Ausgabe im Copy-Constructor reinmache,
erhalte ich folgendes:copy Bus errorWas passiert nun beim Schmeissen einer Excpetion? Wird eine Kopie angelegt?
Wenn ja, wie? magic oder durch Aufruf des Copy-Constructors?
Wenn durch Aufruf des Copy-Constructors, wo ist meine Ausgabe?
Gruß,
CSpilleEDIT: Vielleicht ist der Compiler noch interessant: i686-apple-darwin9-g++-4.0.1
-
Auch wenn ich noch weiß wieso, hier steht, dass ein Copy-Constructor
nötig ist, auch wenn das Objekt nicht kopiert wird:
http://www.parashift.com/c++-faq-lite/exceptions.html#faq-17.1Es scheint mir zudem so, dass ein Objekt, das geschmissen wird, intelligenterweise
(von meinem Compiler) nicht erst auf dem Stack angelegt wird, denn
eine Ausgabe von this im Exception-Constructor zeigt mir folgendes:0xbffff9a0 0xbffff99c 0xbffff998 0xbffff994 0x100280 exceptionalbei dem Code:
try { exception ex1("test"); exception ex2("test"); exception ex3("test"); exception ex4("test"); throw exception("exceptional"); } catch(const exception& e) { std::cerr << e.what() << std::endl; }und die Speicheradressen sind scheinbar völlig verschieden,
was mich zu der Annahme führt: Keine Stack-Verwendung bei throw
-
CSpille schrieb:
Was passiert nun beim Schmeissen einer Excpetion? Wird eine Kopie angelegt?
Wenn ja, wie? magic oder durch Aufruf des Copy-Constructors?
Wenn durch Aufruf des Copy-Constructors, wo ist meine Ausgabe?
Das ist wie bei
return: Normalerweise wird das Objekt kopiert, durch Optimierungen kann eine unnötige Kopie (gerade bei namenlosen temporären Objekten) aber wegfallen. Der Kopierkonstruktor muss jedoch vorhanden sein.
-
Nexus schrieb:
CSpille schrieb:
Was passiert nun beim Schmeissen einer Excpetion? Wird eine Kopie angelegt?
Wenn ja, wie? magic oder durch Aufruf des Copy-Constructors?
Wenn durch Aufruf des Copy-Constructors, wo ist meine Ausgabe?
Das ist wie bei
return: Normalerweise wird das Objekt kopiert, durch Optimierungen kann eine unnötige Kopie (gerade bei namenlosen temporären Objekten) aber wegfallen. Der Kopierkonstruktor muss jedoch vorhanden sein.Stimmt...
Bei return kann die Kopie ja auch wegoptimiert werden...Danke Nexus!!!
-
Für mich als Anfänger sehr intressant zu lesen CSpille! Aber was sollte man jetzt nutzen?
catch(const exception& e)oder
catch(excpetion e)
-
Du solltest Referenzen fangen.
catch (exception& e) catch (const exception& e)
-
Referenz, weil ansonsten der Kopierkonstruktor in der Funktion allenfalls selber eine Exception werfen kann und dann wird abort aufgerufen, was nicht gut ist.