double free or corruption
-
Ich benutze gar kein new/delete, zu Fehler trächtig in der Anfangsentwicklung, von daher müsste der Fehler woanders liegen.
Was Arrays angeht, benutze ich Vectoren, also vorgefertigte Containerklassen.Das ist die Basic_error Klasse
class Basic_error { int zeile; std::string fehlernachricht; public: Basic_error(int,std::string); Basic_error(const Basic_error&); const int& get_zeile() const; const std::string& get_fehlernachricht() const; };Eine Spekulation meinerseits wäre, dass es daran liegen könnte, dass ich in meinem Programm Fehler "Zwischenspeicher" um dann (es ist ein Datei-Parser), erst am Ende des Parsingsvorgang einen Gesamtfehler zu werfen.
Allerdings übergebe ich dabei nie Referenzen/Zeiger an den Speichervector sondern immer nur Kopien :catch(const Parsing_error& e) { //catche Parsingerrors #ifdef debug std::cout << "parsing_error occured " << e.get_fehlernachricht() << std::endl; #endif parsing::fehler.push_back(Parsing_error(e)); //und sammle sie //explizit eine Kopie anfertigen und übergeben parsing::parsing_error_flag = true; //setze die Flag auf true }Hier die initalisierung der Globalen Speichervariable:
namespace parsing { std::vector<Basic_error> fehler = std::vector<Basic_error>(); /*Funktionsdefinitionen*/ }Anmerkung: Basic_error ist die "Mutterklasse" von Parsing_Error. Ich wollte durch das Vererben eine namentliche Trennung zwischen Fehlern beim Parsen und Fehlern beim Interpreten (später) schaffen, im Grunde sind beide Klassen aber Basic_errors.
-
Jud4s schrieb:
namespace parsing { std::vector<Basic_error> fehler = std::vector<Basic_error>(); /*Funktionsdefinitionen*/ }Anmerkung: Basic_error ist die "Mutterklasse" von Parsing_Error. Ich wollte durch das Vererben eine namentliche Trennung zwischen Fehlern beim Parsen und Fehlern beim Interpreten (später) schaffen, im Grunde sind beide Klassen aber Basic_errors.
Wie soll das überhaupt funktionieren, wenn man einen Basisklassenvektor benutzt? Oder benutzt du in wirklichkeit einen vektor aus pointern oder einen ptr_vector?
-
Also funktionieren tut es. Ich weiß nicht worauf du anspielst sonst würde ich probieren das Rätsel zu lösen.
Als ich mit dem Debugger durch gegangen bin (gdb) ist folgendes die letzte Meldung vor dem Speicherdump:
(gdb) next __libc_start_main (main=<value optimized out>, argc=<value optimized out>, ubp_av=<value optimized out>, init=<value optimized out>, fini=<value optimized out>, rtld_fini=<value optimized out>, stack_end=0x7fffffffe178) at libc-start.c:258 258 l i bc-start.c: Datei oder Verzeichnis nicht gefunden. i n libc-start.cIch denke es liegt daran, dass ich eine Datei öffne (via ifstream) aber nie schließe.
Ich habe nun um den ifstream ein Objekt gebastelt:
class File { std::ifstream stream; public: File( const char * filename, std::ios_base::openmode mode = std::ios_base::in ) : stream(filename,mode) { } std::ifstream& get() { return stream; } ~File() { stream.close(); if(stream.fail()) throw std::runtime_error("failbit set"); } };Ich hatte gehofft damit dem Fehler vor zu beugen, da ja jetzt zwangsläufig beim verlassen des Gültigkeitsbereiches auch geschlossen wird.
Allerdings schlägt nun die Exception im Destrukor an, welche auf das failbit prüft, dass beim fehlerhaften schließen gesetzt wird.
-
Streams brauchst du nicht zu schließen, die machen das schon von alleine wenn sie den Scope verlassen. Wenn du noch andere solche "Verbesserungen" im Programm hast, kommen wir der Sache langsam auf die Spur.
-
Lass das Programm mal mit valgrind laufen und sieh nach, ob es Fehler meldet.
-
Außerdem: Aus einem Destruktor heraus eine Exception zu werfen kann recht böse ausgehen. Der Destruktor wird nämlich auch aufgerufen, wenn das Objekt bei der Behandlung einer anderen Exception aufgerufen wird - und wenn du dann eine zweite Exception hinterherwirfst, weiß das Programm nicht mehr, was es machen soll.
-
Hab ich jetzt einen Stahlbetonträger vor dem Kopf oder kann man bei einem std::vector<T> doch nur T speichern, und keine abgeleiteten Klassen? In Java wär das ja was ganz anderes. Oder Mit einem std::vector<T*>.
-
Java ist kein C++. Nimm einen vector<T*>.
-
Cachus schrieb:
Hab ich jetzt einen Stahlbetonträger vor dem Kopf oder kann man bei einem std::vector<T> doch nur T speichern, und keine abgeleiteten Klassen?
Das hast du richtig erkannt - ein vector<T> speichert T-Objekte. Und wenn du versuchst, etwas anderes reinzupacken, greift das Slicing (d.h. das übergebene Objekt wird in ein blankes T zurechtgestutzt). Für polymorphe Container (die auch abgeleitete Klassen verarbeiten können) benötigst du Zeiger (entweder nackt wie in vector<T*> oder versteckt in einem boost::ptr_vector<T>).
-
Cachus schrieb:
Hab ich jetzt einen Stahlbetonträger vor dem Kopf oder kann man bei einem std::vector<T> doch nur T speichern, und keine abgeleiteten Klassen? In Java wär das ja was ganz anderes. Oder Mit einem std::vector<T*>.
Hast du völlig recht...
Es wird der Copy-Konstruktor der Basisklasse aufgerufen, wodurch das Verhalten wohl anders ist als gewollt...
-
Das hast du richtig erkannt - ein vector<T> speichert T-Objekte. Und wenn du versuchst, etwas anderes reinzupacken, greift das Slicing (d.h. das übergebene Objekt wird in ein blankes T zurechtgestutzt). Für polymorphe Container (die auch abgeleitete Klassen verarbeiten können) benötigst du Zeiger (entweder nackt wie in vector<T*> oder versteckt in einem boost::ptr_vector<T>).
Wie vermeide ich bei einer solchen Lösung Speicherlecks, weil da muss ich ja zwangsläufig die Fehler auf dem Heap initialisieren.
Hmm, daran mag es liegen. Was meine "Verbesserung" angeht, das sollte eigentlich nur eine Schnelllösung darstellen, was anderes von dieser Art habe ich nicht.
-
Indem du boost::ptr_vector verwendest, oder einen vector<unique_ptr> oder einen vector<shared_ptr>
-
Jud4s schrieb:
Das hast du richtig erkannt - ein vector<T> speichert T-Objekte. Und wenn du versuchst, etwas anderes reinzupacken, greift das Slicing (d.h. das übergebene Objekt wird in ein blankes T zurechtgestutzt). Für polymorphe Container (die auch abgeleitete Klassen verarbeiten können) benötigst du Zeiger (entweder nackt wie in vector<T*> oder versteckt in einem boost::ptr_vector<T>).
Wie vermeide ich bei einer solchen Lösung Speicherlecks, weil da muss ich ja zwangsläufig die Fehler auf dem Heap initialisieren.
Der ptr_vector kümmert sich afaik darum, die Elemente per delete freizugeben, wenn er zerstört wird. Bei einem normalen vector muß sich der Besitzer darum kümmern, vor seiner Vernichtung die Elemente zu delete'n.
(Alternativ kannst du Smart-Pointer ala boost::shared_ptr oder std::unique_ptr (C++0x) in den vector packen - die räumen auch hinter sich auf)