double free or corruption



  • 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.c
    

    Ich 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.


  • Mod

    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)


Anmelden zum Antworten