push_back pointer



  • Guten Morgen,

    ich habe ein Problem damit, an einen Vector mittels push_back einen Neuen Pointer zu hängen. Im Source sieht das so aus:

    Der Header:

    #ifndef TREEH
    #define TREEH
    
    // Standart Libs einbinden
    #include <string>
    #include <vector>
    
    class node{
    public:
    	node();//
    	node(char *par);//
    	node(std::string &par);//
    	virtual ~node();//
    
    	std::string get_name() const;
    	void set_name(std::string &par);//
    	unsigned int get_nr_children() const; //
    	node* get_child(unsigned int i) const; //
    	void add_child(node& child); //
    private:
        std::string Inhalt;
        std::vector<node*> next;
    
    };
    
    #endif
    

    Die Funktion:

    void node::add_child(node &child) {
        std::string buffer = child.get_name();
        node *neu = new node(buffer);
        next.push_back(neu);
    }
    

    Compilieren lässt sich das Programm, aber ich kriege zur Laufzeit einen Fehler.

    *** glibc detected *** /home/fettpet/Dokumente/cpp/ECG 1/Treex/Treex/Tree/bin/Debug/Tree: double free or corruption (fasttop): 0x084e2020 ***
    ======= Backtrace: =========
    /lib/i386-linux-gnu/libc.so.6(+0x6ff22)[0xb92f22]
    /lib/i386-linux-gnu/libc.so.6(+0x70bc2)[0xb93bc2]
    /lib/i386-linux-gnu/libc.so.6(cfree+0x6d)[0xb96cad]
    /usr/lib/i386-linux-gnu/libstdc++.so.6(_ZdlPv+0x1f)[0x67880f]
    /usr/lib/i386-linux-gnu/libstdc++.so.6(_ZNSs4_Rep10_M_destroyERKSaIcE+0x1b)[0x65fc4b]
    /usr/lib/i386-linux-gnu/libstdc++.so.6(+0x94c8c)[0x65fc8c]
    /usr/lib/i386-linux-gnu/libstdc++.so.6(_ZNSsD1Ev+0x2e)[0x65fcfe]
    /home/fettpet/Dokumente/cpp/ECG 1/Treex/Treex/Tree/bin/Debug/Tree[0x8048d0a]
    /home/fettpet/Dokumente/cpp/ECG 1/Treex/Treex/Tree/bin/Debug/Tree[0x8048d67]
    /home/fettpet/Dokumente/cpp/ECG 1/Treex/Treex/Tree/bin/Debug/Tree[0x8048cd2]
    /home/fettpet/Dokumente/cpp/ECG 1/Treex/Treex/Tree/bin/Debug/Tree[0x8048aab]
    /lib/i386-linux-gnu/libc.so.6(__libc_start_main+0xf3)[0xb3c113]
    /home/fettpet/Dokumente/cpp/ECG 1/Treex/Treex/Tree/bin/Debug/Tree[0x8048991]
    ======= Memory map: ========
    00110000-00138000 r-xp 00000000 08:01 266318     /lib/i386-linux-gnu/libm-2.13.so
    00138000-00139000 r--p 00028000 08:01 266318     /lib/i386-linux-gnu/libm-2.13.so
    00139000-0013a000 rw-p 00029000 08:01 266318     /lib/i386-linux-gnu/libm-2.13.so
    005cb000-006a9000 r-xp 00000000 08:01 1052115    /usr/lib/i386-linux-gnu/libstdc++.so.6.0.16
    006a9000-006aa000 ---p 000de000 08:01 1052115    /usr/lib/i386-linux-gnu/libstdc++.so.6.0.16
    006aa000-006ae000 r--p 000de000 08:01 1052115    /usr/lib/i386-linux-gnu/libstdc++.so.6.0.16
    006ae000-006af000 rw-p 000e2000 08:01 1052115    /usr/lib/i386-linux-gnu/libstdc++.so.6.0.16
    006af000-006b6000 rw-p 00000000 00:00 0 
    00a6f000-00a8b000 r-xp 00000000 08:01 263098     /lib/i386-linux-gnu/libgcc_s.so.1
    00a8b000-00a8c000 r--p 0001b000 08:01 263098     /lib/i386-linux-gnu/libgcc_s.so.1
    00a8c000-00a8d000 rw-p 0001c000 08:01 263098     /lib/i386-linux-gnu/libgcc_s.so.1
    00b23000-00c9b000 r-xp 00000000 08:01 266314     /lib/i386-linux-gnu/libc-2.13.so
    00c9b000-00c9d000 r--p 00178000 08:01 266314     /lib/i386-linux-gnu/libc-2.13.so
    00c9d000-00c9e000 rw-p 0017a000 08:01 266314     /lib/i386-linux-gnu/libc-2.13.so
    00c9e000-00ca1000 rw-p 00000000 00:00 0 
    00cb7000-00cb8000 r-xp 00000000 00:00 0          [vdso]
    00e1a000-00e38000 r-xp 00000000 08:01 262156     /lib/i386-linux-gnu/ld-2.13.so
    00e38000-00e39000 r--p 0001d000 08:01 262156     /lib/i386-linux-gnu/ld-2.13.so
    00e39000-00e3a000 rw-p 0001e000 08:01 262156     /lib/i386-linux-gnu/ld-2.13.so
    08048000-0804b000 r-xp 00000000 00:14 13109429   /home/fettpet/Dokumente/cpp/ECG 1/Treex/Treex/Tree/bin/Debug/Tree
    0804b000-0804c000 r--p 00002000 00:14 13109429   /home/fettpet/Dokumente/cpp/ECG 1/Treex/Treex/Tree/bin/Debug/Tree
    0804c000-0804d000 rw-p 00003000 00:14 13109429   /home/fettpet/Dokumente/cpp/ECG 1/Treex/Treex/Tree/bin/Debug/Tree
    084e2000-08503000 rw-p 00000000 00:00 0          [heap]
    b7600000-b7621000 rw-p 00000000 00:00 0 
    b7621000-b7700000 ---p 00000000 00:00 0 
    b772e000-b7731000 rw-p 00000000 00:00 0 
    b7745000-b7748000 rw-p 00000000 00:00 0 
    bf9b9000-bf9da000 rw-p 00000000 00:00 0          [stack]
    Aborted
    

    Wo liegt mein Fehler?


  • Mod

    Du hast in deinem vector besitzende Zeiger gespeichert ( ⚠ Bist du wirklich ganz sicher, dass du das möchtest?). Da du auch selber richtigerweise eine Destruktor deklariert hast, nehme ich mal an, dass du in diesem ein delete auf diese Zeiger aufrufst (was auch richtig wäre). Was dann aber nicht mehr richtig wäre, wäre dass du keinen eigenen Zuweisungsoperator und Kopierkonstruktor erstellt hast. Dann wird nämlich, falls eine Instanz deiner Klasse kopiert oder zugewiesen wird (was sehr leicht unbemerkt und/oder ungewollt passieren kann!) eine flache Kopie der Zeiger erstellt (d.h. das wodrauf sie zeigen wird nicht kopiert) und du hast zwei Objekte, die beide auf die gleichen nodes zeigen. Und beide zerstören sie im Destruktor diese gleichen nodes. Und dann hast du das was die Fehlermeldung besagt, nämlich ein doppeltes free.

    Abhilfen wären:
    1. Du definierst die beiden fehlenden Funktionen entsprechend (Stichwort: Regel der großen Drei)
    2. Oder eleganter: Du wrappst den vector in eine Klasse, die darauf spezialisiert ist, besitzende Pointer zu speichern und selbstständig tiefe Kopien und das Löschen übernimmt (Stichwort: ptr_vector)

    Oder es ist was ganz anderes und du hast nicht den relevanten Code gezeigt. Es wäre einfacher zu helfen, wenn du Beispiele postest, die tatsächlich compilierbar sind, an denen ein Helfer den Fehler bei sich auf dem Rechner nachvollziehen kann.

    P.S.: Und du solltest etwas mehr auf const-correctness achten. Die Membermethoden machst du ja schon ordentlich const, aber sollen die Parameter von set_name und add_child wirklich nicht-const sein?
    P.P.S.: Einfach verkettete Listen brauchst du übrigens nicht selber programmieren, die gibt es wie Sand am Meer. Und seit neuestem auch in der Standardbibliothek (Stichwort: std::forward_list)



  • @SeppJ Warum schlägst du vector_ptr vor vector<unique_ptr> vor?


  • Mod

    cooky451 schrieb:

    @SeppJ Warum schlägst du vector_ptr vor vector<unique_ptr> vor?

    Gewohnheit.



  • Hallo,

    Danke für die Antwort. Ich habe einen Zuweißungsoperator und einen Kopierkonstruktor definiert.
    Der Fehler liegt wirklich im Destruktor

    node::~node() {
        for(unsigned int i=0; i< next.size(); ++i) {
            next[i]->~node() ;
            delete next[i];
        }
    }
    

    wenn ich entweder next[i]->node() oder delete rausnehme, verschwindet der Fehler. Ich weiß nur nicht welches das ist, was drin bleiben muss. Eine kurze Begrüdundung währe cool.

    hier mal noch der Restliche Source

    tree.cpp

    #include "tree.h"
    
    /*
        Konstruktoreen
    */
    // Die schön sind
    node::node(){}
    node::node(char *par){
        Inhalt = par;
    }
    
    // Der Geforderte
    node::node(std::string &par){
        Inhalt = par;
    }
    
    // Der Destruktors
    node::~node() {
        for(unsigned int i=0; i< next.size(); ++i) {
            next[i]->~node() ;
            delete next[i];
        }
    }
    /*
        Restlichen Funktionen
    */
    
    // Set Name
    void node::set_name(std::string &par) {
        Inhalt = par;
    }
    
    // Get Name
    std::string node::get_name() const{
        return Inhalt;
    }
    
    // Adden
    void node::add_child(node &child) {
        node *neu = new node(child);
        next.push_back(neu);
    }
    
    unsigned int node::get_nr_children() const{
        return next.size();
    }
    
    node* node::get_child(unsigned int i) const{
        if(i < next.size()) {
            return next[i];
        }
    }
    
    node::node(const node &par){
        Inhalt = par.get_name();
        next = par.next;
    }
    
    node& node::operator=(const node &par){
        Inhalt = par.get_name();
        next = par.next;
    }
    

    main.cpp

    // Treex.cpp : Definiert den Einstiegspunkt für die Konsolenanwendung.
    //
    #include <iostream>
    #include <cstdlib>
    #include <string>
    #include "../tree.h"
    
    using namespace std;
    
    int main()
    {
    	node Baum("Baum"), Neuer("Neuer");
    
        Baum.add_child(Neuer);
    	//cout << Baum.get_name();
    
    	return 0;
    }
    


  • Fettpet schrieb:

    Der Fehler liegt wirklich im Destruktor

    node::~node() {
        for(unsigned int i=0; i< next.size(); ++i) {
            next[i]->~node() ;
            delete next[i];
        }
    }
    

    wenn ich entweder next[i]->node() oder delete rausnehme, verschwindet der Fehler. Ich weiß nur nicht welches das ist, was drin bleiben muss. Eine kurze Begrüdundung währe cool.

    next[i]->~node() muss raus. Explizite Destruktor-Aufrufe machen nur in Verbindung mit Placement-New sinn, im Normalfall braucht man die nie.



  • Kurze Frage: Wer in aller Welt hat Dir beigebracht dass man Destruktoren manuell aufruft?

    (Kurzer Hinweis: Die Antwort auf die Frage, welches drinbleiben muss, versteckt sich in obiger Frage)


  • Mod

    Fettpet schrieb:

    Hallo,

    Danke für die Antwort. Ich habe einen Zuweißungsoperator und einen Kopierkonstruktor definiert.

    Du hast sie aber so definiert, wie es auch der Compiler naiv machen würde (wobei du ein return vergessen hast). Es passiert genau das was ich oben beschrieben habe. In der Zeile mit dem push_back erstellst du nämlich eine Kopie eines nodes und für beide Kopien wird der Destruktor aufgerufen, aber beide haben Zeiger auf den gleichen Speicher.

    Der Fehler liegt wirklich im Destruktor

    Mag sein, aber die Ursache liegt (neben deinem schon genannten direkten Fehler mit dem manuellen Destruktoraufruf) ganz woanders, siehe oben.


Anmelden zum Antworten