Smartpointer und Baumadressierung



  • Hallo, habe ein Problem mit einer Klassenhierachie.
    Ich habe meinen Code etwas vereinfacht damits übersichtlicher wird.
    Ziel soll später mal eine Art Baumstruktur werden.
    Im Prinzip "besitzen" Instanzen meiner "myClass" weitere Instanzen von "myClass".

    Hier mal mein Code, zu dem ich dann ein paar Fragen habe

    #include <iostream>
    using namespace std;
    
    class myClass
    {
    	std::string name;
    	myClass *parent;
    	myClass *child;
    public:
    	myClass(std::string name) : name(name), parent(0), child(0) { cout << "created " << name << endl; }
    	myClass(std::string name, myClass *parent, myClass* child) : name(name), parent(parent), child(child) { cout << "created " << name << endl; }
    	~myClass(void);
    
    	void addChild(std::string name);
    	std::string getName(void) { return name; }
    	myClass *getChild(void) { return child; }
    };
    
    myClass::~myClass(void)
    {
    	cout << "destroyed " << name << endl;
    }
    
    void myClass::addChild(std::string name)
    {
    	child = new myClass(name, this, 0);
    }
    
    int main()
    {
    	myClass root("root");
    
    	root.addChild("child");
    
    	cout << root.getName() << endl;
    
    	root.getChild()->addChild("childchild");
    
    	return 0;
    }
    
    1. Das große Problem ist offensichtlich und wird auch durch die Konsolenausgabe bestätigt: ich habe Speicherlecks.
      Das könnte ich lösen indem ich im Destruktor einfach ein "delete child;" einfüge. Aber das ist unschön, wie ich mittlerweile weiß. Sowas löst mit Smart-, genauer shared_ptr.
      Nun bin ich mir nicht sicher wo ich mein "myClass *" überall durch einen shared_ptr ersetzen muss.
      Beim Kompilieren gibts natürlich im Konstruktor Fehlermeldungen... (an der Stelle parent(0), child(0)). Wie gehe ich hier vor?

    2. Um an einenn Knoten einen weiteren Knoten anzuheften (mittels addChild()) brauche ich einen Zeiger darauf. Ich halte es für eher schlechte Praxis, zeiger auf die Interna meines Baumes herauszurücken. Aber mir fällt keine andere Möglichkeit ein, den zu erweiterten Knoten zu addresieren.
      Hab mir auch überlegt meine Knoten in einer weiteren Klasse "myBaum" zu verstecken. Dadurch verlagere ich das Problem aber nur. Irgendwie muss ja ein Außenstehender angeben können, welcher Knoten behandelt werden soll.

    Könnt ihr mir da weiterhelfen?

    Danke & Gruß!

    PS: ich hab grad gesehn, momentan ist ein ganz Ähnliches Topic offen. Ich habe mich aber dagegen entschieden, den anderen Thread zu missbrauchen, lese da aber auch gespannt mit.



  • Aus welchem Buch lernst Du?



  • Nicht aus dem, gegen dass du hier irgendeine Kampagne führst.
    Ansonsten würde ich dich bitten, meine Thread nicht in irgendeine Richtung zu lenken.



  • lugge86 schrieb:

    Beim Kompilieren gibts natürlich im Konstruktor Fehlermeldungen... (an der Stelle parent(0), child(0)). Wie gehe ich hier vor?

    Das sind Smartpointer, die initialisieren sich alleine.

    lugge86 schrieb:

    1. Um an einenn Knoten einen weiteren Knoten anzuheften (mittels addChild()) brauche ich einen Zeiger darauf. Ich halte es für eher schlechte Praxis, zeiger auf die Interna meines Baumes herauszurücken. Aber mir fällt keine andere Möglichkeit ein, den zu erweiterten Knoten zu addresieren.
      Hab mir auch überlegt meine Knoten in einer weiteren Klasse "myBaum" zu verstecken. Dadurch verlagere ich das Problem aber nur. Irgendwie muss ja ein Außenstehender angeben können, welcher Knoten behandelt werden soll.

    Was soll dein Baum denn tun? Eine std::map ist intern auch ein Baum, aber von aussen sieht man das nicht.

    Dein Ein-Kind-Baum ist aber eher was für China, oder?



  • manni66 schrieb:

    Dein Ein-Kind-Baum ist aber eher was für China, oder?

    😃 👍
    wäre das nicht eher eine doppelt verkettete liste?

    ontopic:
    der zeiger auf den knoten darüber soll weiterhin ein zeiger bleiben. rohe zeiger sind in c++ völlig in ordnung, solange sie nicht besitzen. für den zeiger auf die unteren äste / blätter würde ich eher std::unique_ptr nehmen. std::shared_ptr sieht zwar auf den ersten blick aus wie eine lösung für alle probleme, bringt aber auch overhead mit sich. ich sehe nicht wieso zwei knoten die selben äste haben können sollten.



  • manni66 schrieb:

    Dein Ein-Kind-Baum ist aber eher was für China, oder?

    Natürlich, die Ein-Knoten-Politik 😉
    Ne, wie gesagt, mein Beispiel ist vereinfacht. Später wird der Kind-Zeiger durch eine Kind-Map ersetzt, dann wirds auch ein richtiger Baum.

    Fragt sich nur noch, wie ich die Knoten von außen addresiere. Einen Zeiger herausgeben und mihc Knoten für Knoten vorangeln? Glaub nicht dass das so optimal ist...

    Danke & Gruß,
    lugge



  • lugge86 schrieb:

    Fragt sich nur noch, wie ich die Knoten von außen addresiere. Einen Zeiger herausgeben und mihc Knoten für Knoten vorangeln? Glaub nicht dass das so optimal ist...

    mit einer iterator-klasse. die zeigt dann gleich auf das richtige blatt. wenn du dann damit navigieren willst dann musst du eben den zu deinem baum passenden algorithmus implementieren.



  • Ok, ich habe mal meinen Code so umgeschrieben, dass ich zunächst nur *child durch einen shared_ptr ersetzt habe.
    Wie müsste nun meine addChild() Funktion aussehen?
    Funktionieren tut

    void myClass::addChild(std::string name)
    {
    	shared_ptr<myClass> ptr(new myClass(name, this));
    	child = ptr;
    }
    

    Da bin ich mir aber nicht sicher ob man das so macht. child wird ja im Konstruktor von myClass zunächst schon erzeugt.
    in addChild() erzeuge ich aber so ein komplett neues Objekt und mache dann eine Zuweisung. Passt das so?

    Auserdem ist meine Ausgabe:

    created root
    created child
    created childchild
    destroyed root
    destroyed child
    destroyed childchild
    

    Von der raw-Pointer Methode bin ich gewohnt dass die Kinder in invertierter Reihenfolge zerstört werden, also

    created root
    created child
    created childchild
    destroyed childchild
    destroyed child
    destroyed root
    

    Kann ich so irgendwelche Nachteile haben? Passt das so?

    Danke & Gruß



  • hast du meinen post nicht gelesen?

    asfdlol schrieb:

    der zeiger auf den knoten darüber soll weiterhin ein zeiger bleiben. rohe zeiger sind in c++ völlig in ordnung, solange sie nicht besitzen. für den zeiger auf die unteren äste / blätter würde ich eher std::unique_ptr nehmen. std::shared_ptr sieht zwar auf den ersten blick aus wie eine lösung für alle probleme, bringt aber auch overhead mit sich. ich sehe nicht wieso zwei knoten die selben äste haben können sollten.

    mach den member der auf den knoten darunter zeigt zu einem std::unique_ptr .

    dann:

    void myClass::addChild(std::string name)
    {
        this->child.reset(new myClass(name, this));
    }
    


  • Danke dir, genau die reset-Methode hat mir gefehlt.

    Das mit shared vs unique hab ich gelesen. Wird auch noch abgeändert, aber momentan gehts mir noch um das grundsätzliche Vorgehen.

    Kannst du noch was zu meiner zweiten Frage (Destruktor-Reihenfolge) sagen?



  • mit der smart-pointer-methode hast du zuerst den destruktor des obersten knotens aufgerufen (implizit), worauf hin die meldung nach std::cout ging. als das fertig war wurde der destruktor des nächst-oberen knotens aufgerufen usw...

    bei der raw-methode hast du vermutlich diese reihenfolge (die des std::cout's und des destruktors der member) vertauscht indem du delete aufgerufen hast bevor du nach std::cout geschrieben hast. daher sind da die tiefsten knoten zuerst gekommen.



  • lugge86 schrieb:

    Nicht aus dem, gegen dass du hier irgendeine Kampagne führst.
    Ansonsten würde ich dich bitten, meine Thread nicht in irgendeine Richtung zu lenken.

    Mir ist klar, daß es nicht Wolf ist. Dann schick mir den Buchnamen per PN.


Log in to reply