Zeiger auf Ende einer Objektkette setzen



  • [Closed] - Bedanke mich für die Hilfe.

    Hallo!

    Ich versuche also eine Objektkette zu gestalten und nach jedem Anhängen eines neuen Elements gleich zu wissen, wo sich das Ende befindet. Also ein Zeiger auf den Anfang der Kette, einen Zeiger auf das Ende. Das neue Objekt soll gleich ans Ende gehängt werden. Momentan geschieht in etwa Folgendes (hoffe der Code ist nicht zu lang, fasse unten das Verhalten zusammen).

    #include <iostream>
    using namespace std;
    
    class a { // Klasse wird definiert
        a* naechsterEintrag;
        string aInhalt;
    public:
    ersteDaten()     
    void listeErweitern(string inhalt);
    }
    // Erste Daten
    void a::ersteDaten() {
        aInhalt = 0;
        naechsterEintrag = nullptr;
    }
    // Funktion zum erweitern der Liste
    void a::listeErweitern(string inhalt) {
        naechsterEintrag = new a(); // Neuer Link am Ende der Kette
        naechsterEintrag = naechsterEintrag->naechsterEintrag  // Aufs naechste Element springen
        aInhalt = inhalt;
        naechsterEintrag  = nullptr;
    }
    
    int main () {
        a* anfangDerListe;
        a* endeDerListe;
        // Anfang festlegen
        anfangDerListe = new a(); // Neuer Link hinzugefuegt
        anfangDerListe->ersteDaten();
        endeDerListe = anfangDerListe;
        // An Liste anhaengen
        endeDerListe->listeErweitern("1");
        endeDerListe->listeErweitern("2");
        ...
        
        return 0;
    }
    

    Ich versuche hiermit also an das Ende der Kette ein neues Element anzuhängen. Ich sehe bereits das Problem, dass der Befehl:

        endeDerListe = anfangDerListe;
    

    ...dazu führt, dass beide Zeiger identisch sind und die gleiche Adresse haben, der Anfang der Liste darf sich aber nicht verändern, da ich später eine Funktion aufrufe, die vom Anfang bis zum Ende durchgehen und alle "aInhalt" Werte der Kette (von Anfang bis Ende) ausgeben soll. Ich habe auch schon versucht, per Funktion nur den Wert für "endeDerListe->naechsterEintrag" auf "anfangDerListe->naechsterEintrag" zu setzen. Dadurch sind die beiden Zeiger nicht mehr identisch, aber "anfangDerListe" und "endeDerListe" scheinen voneinander komplett unabhängig.

    Hat jemand ne Ahnung was ich falsch mache? Bin mir nicht sicher, wie ich es ändern muss, sodass "endeDerListe" immer auf das letzte Element der gleichen Liste gesetzt ist.

    Vielen Dank im Voraus und lieber Gruß



  • Der übliche Name für Objekltkette ist verkettete Liste.

    listeErweitern muss das neue Ende zurückliefern:

    endeDerListe = endeDerListe->listeErweitern("1");
    

    In C++ sollte die Liste selbst in einer Klasse gekapselt sein (siehe std::list).



  • @manni66 Hi Manni,

    danke für deine schnelle Antwort! Damit habe ich auch experimentiert, habe aber keine zuverlässigen Ergebnisse bekommen (vielleicht nicht richtig formuliert im Code). Ist mein Verständnis richtig, dass ich dann die Funktion "void listeErweitern()" zu einem "a* listeErweitern()" für den Rückgabewert ändern muss? Das hab ich schon mehrmals probiert, aber hat nicht geklappt.

    Habe auch gesehen, dass es die Möglichkeit für eine separate Klasse für die Liste gibt - für die Lösung hierfür muss ich allerdings bei der gleichen Klasse bleiben.

    Vielen Dank!



  • Ja, dass musst du machen.

    Aber du setzt in listeErweitern naechsterEintrag auf nullptr, damit kommst du an dein gerade erstelltes a Objekt nicht mehr dran und du hast einen hervorragenden Memory Leak gebaut.



  • @Schlangenmensch Hi Schlangenmensch,

    danke auch Dir für Deine Antwort. Den Wert für "naechsterEintrag" wird beim Erstellen jedes neuen Elements auf "nullptr" gesetzt, um das Ende zu kennzeichnen. Wenn also später der Aufruf der Liste erfolgt, soll der aInhalt ausgegeben werden und wenn "naechsterEintrag != nullptr" dann soll die Funktion zum nächsten Element springen und das auch ausgeben. Müsste ich das abändern?

    Mein Problem beim Umbau von der Funktion "listeErweitern" ist, dass ich die Werte für "naechsterEintrag" nicht mehr über die Funktion ändern kann, da es ein private member der Klasse ist. Ich versuche daher, in der Funktion "listeErweitern" über eine neue Funktion die Werte zu ändern und dann das Ende der Liste über return zurückzugeben, aber es funktioniert nicht so ganz ...

    Edit: Das Problem ist auch, dass wenn ich "listeErweitern" wie oben beschrieben abändere, ich noch das Listenende als Argument mit reinnehmen muss - oder gibt es da ne bessere Lösung?

    Hättet ihr evtl. hierfür einen Ansatz? Hoffe ich hab mich verständlich ausgedrückt. Sitze schon seit vielen Stunden an dem Problem und verstehe nicht wirklich, was genau der Fehler ist.

    Vielen Dank!



  • @Masch sagte in Zeiger auf Ende einer Objektkette setzen:

    dass ich die Werte für "naechsterEintrag" nicht mehr über die Funktion ändern kann, da es ein private member der Klasse ist.

    ???

    listeErweitern ist eine Memberfunktion



  • @Masch

    void listeErweitern(string inhalt) {
        naechsterEintrag = new a(); //Hier estellst du ein neues Objekt und lässt naechster Eintrag darauf zeigen
        /*
        Jetzt nimmst du den naechterEintrag Pointer von einer Stelle weiter und packst die Adresse in die naechsterEintrag
        Variable von dem aktuellen Listeneintrag... jetzt kommst du an dein grade erstelltes a schon nicht mehr dran
       */
        naechsterEintrag = naechsterEintrag->naechsterEintrag 
        aInhalt = inhalt;
        naechsterEintrag  = nullptr;
    }
    

    Was du haben willst, ist:

    a* listeErweitern(string inhalt) {
        naechsterEintrag = new a();
        naechsterEintrag->naechsterEintrag = nullptr; //Würde ich eigentlich im Konstruktor von a machen 
        aInhalt = inhalt;
        return naechsterEintrag;
    }
    


  • Wenn man eine Liste will sollte man auch eine Liste haben.

    class node {
        friend class list;
    
        std::string data;
        node *next = nullptr;
    
    public:
        node(std::string data) : data{ data } {}
    };
    
    class list {
        node *head = nullptr;
        node *tail = nullptr;
    
    public:
        append(std::string data)
        {
            if (!head) {
                head = tail = new node{ data };
                return;
            }
    
            tail->next = new node{ data };
            tail = tail->next;
        }
    
        list(list const&) = delete;
    
        ~list()
        {
            auto current{ head };
            while (current) {
                auto temp{ current->next };
                delete current;
                current = temp;
            }
        }
    };
    


  • Danke euch allen für die Antworten!

    @Manni: Was ich meinte ist, dass wenn ich "listeErweitern" (so wie ich es oben habe) ändere zu "a* listeErweitern(string inhalt)", kann ich über main() die Werte für endeDerListe nicht mehr ändern, also:

    [...]
    // Funktion zum erweitern der Liste
    a* listeErweitern(string inhalt) {
        // Vorschlag von Schlangenmensch hier übernommen
        naechsterEintrag = new a();
        naechsterEintrag->naechsterEintrag = nullptr;
        aInhalt = inhalt;
        return naechsterEintrag;
    /*
    Hier bekomme ich nun unzaehlige Fehler mit 
    "reference to overload function could not be resolved". 
    Zudem ist "endeDerListe" in dieser Funktion nicht bekannt,
    da der Zeiger in "main()" erstellt wird...
    */
    }
    [...]
    main() {
        [...]
        endeDerListe = endeDerListe->listeErweitern("1");
        [...]
    }
    

    Soweit ich weiß liegt das daran, dass "naechsterEintrag" hier nicht bekannt ist, weil es ein private member von class a ist, oder? ... wird bei mir wohl ein Problem mit meinem Verständnis von C++ und Zeigern sein - programmiere noch nicht so lange mit C++ und lerne - vielen Dank für die Hilfe!

    @Schlangenmensch: Ich dachte mit "naechsterEintrag = naechsterEintrag->naechsterEintrag" setze ich den Zeiger auf "naechsterEintrag" des nächsten Elements, welches dann =nullptr ist. Versuche immer noch bei der Logik durchzublicken - wie kann ich denn dann bei deinem Vorschlag am besten durch Main() mit dem Rückgabewert arbeiten?

    @Swordfish: Danke! Von der Option habe ich auch gehört (wahrscheinlich der bessere Weg) aber für meine Aufgabenstellung muss ich es bei der einen Klasse behalten und lediglich mit einem Zeiger arbeiten, der aufs Ende der einfachen Kette zeigt.

    Danke euch allen, hoffe ihr habt hier nochmal nen kleinen Vorschlag für das Problem. Lieber Gruß!



  • listeErweitern sollte so aussehen, denn du brauchst das ende um da den Zeiger des nächsten einzutragen

    a* listeErweitern(a* ende, string inhalt) {
        naechsterEintrag = new a(); // Neuer Link am Ende der Kette
        if(ende)  //Prüfen ob ende ungleich NULL ist
                ende->naechsterEintrag  = naechsterEintrag ; //vom letzen element den nachfolger sezten
        naechsterEintrag ->aInhalt = inhalt; //Inhalt des elementes beschreiben
        naechsterEintrag->naechsterEintrag   = nullptr; //solle in den Constructor verlegt werden
    }
    

    aufruf wäre dann so in der Main:

    endeDerListe = endeDerListe->listeErweitern(endeDerListe , "1");
    

    Dann sollte das passen. wobei naechsterEintrag->naechsterEintrag = nullptr; würde ich wie schon gesagt im Constructor der Klasse mit machen.



  • @Masch [...] und "unzaehlige Fehler mit" sind völlig nutzlos. Code und Fehlermeldungen: Copy&Paste.



  • @Masch sagte in Zeiger auf Ende einer Objektkette setzen:

    Soweit ich weiß

    Vollständigen Kot or no help.



  • @Masch sagte in Zeiger auf Ende einer Objektkette setzen:

    @Schlangenmensch: Ich dachte mit "naechsterEintrag = naechsterEintrag->naechsterEintrag" setze ich den Zeiger auf "naechsterEintrag" des nächsten Elements, welches dann =nullptr ist. Versuche immer noch bei der Logik durchzublicken - wie kann ich denn dann bei deinem Vorschlag am besten durch Main() mit dem Rückgabewert arbeiten?

    Ja und wie willst du dann an das nächste wirkliche Element dran kommen. Also, an den Inhalt des neu kreierten a?

    Naja, in der main()steht dann halt sowas:

    a* lastElement = previousElement->listeErweitern("foo");
    

    Bzw eigentlich gehört das, wie hier mehrfach schon gesagt, in eine eigene Klasse 😉



  • @Masch sagte in Zeiger auf Ende einer Objektkette setzen:

    für meine Aufgabenstellung

    Was Du da gelehrt bekommst ist halt einfach nur Blödsinn. Das ist von einem der C kann aber Datenkapselung nie verstanden hat. Nutzlos.



  • @CTecS Danke CTecS! Genau das war auch mein ursprünglicher Gedanke es so zu machen, hier stoße ich nur auf das Problem mit der Fehlermeldung (@Manni: Sorry dafür, Fehlermeldung unten). Heißt also:

    1. in "a* listeErweitern" gibt mir jedes Element für "naechsterEintrag" (und für "aInhalt")die Meldung:
      main.cpp:150:18: error: 'naechsterEintrag' is a private member of 'listenelement'
      main.cpp:135:20: note: implicitly declared private here

    2. Zum Test habe ich daher sowohl "naechsterEintrag" als auch "aInhalt" in der Klasse als public deklariert. So kriege ich zwar diese Fehlermeldung nicht mehr (soll so aber nicht sein), stattdessen meckert das Programm bei jedem "endeDerListe = endeDerListe ->anhaengen("2", endeDerListe );" in main(); mit der folgenden Meldung:
      main.cpp:188: Fehler: undefined reference to `a::listeErweitern(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, a*)'

    Füge hier einmal den ganzen Code ein, den ich nun habe (sorry für die Länge):

    #include <iostream>
    using namespace std;
    
    class a {
        string aInhalt;
        a* naechsterEintrag;
    public:
        void ersterEintrag(string inhalt);
        a* listeErweitern(string inhalt, a* endeDerListe);
        void listeAusgeben();
        void listeLoeschen();
    };
    
    void a::ersterEintrag(string inhalt) {
        aInhalt = inhalt;
        naechsterEintrag = nullptr;
    }
    // Element an Liste anhaengen - hier wird gemaeckert, dass
    // "naechsterEintrag" a private member ist, Fehler 1) oben.
    a* listeErweitern(string inhalt, a* endeDerListe) {
        naechsterEintrag = new a();
        if (endeDerListe) 
            endeDerListe->naechsterEintrag = naechsterEintrag;
        naechsterEintrag->aInhalt = inhalt;
        naechsterEintrag->naechsterEintrag = nullptr;
    
        return endeDerListe;
    }
    // Alle Elemente ausgeben
    void a::listeAusgeben() {
        cout << aInhalt << '\n';
        if (naechsterEintrag != nullptr) {
            naechsterEintrag->listeAusgeben();
        }
    }
    // Speicher freigeben
    void a::listeLoeschen() {
        if (naechsterEintrag != nullptr) {
            naechsterEintrag->listeLoeschen();
            delete(naechsterEintrag);
        }
    }
    
    int main () {
        // Zeiger auf Listenanfang und Ende
        a* anfangDerListe;
        a* endeDerListe;
        // Erster Eintrag
        anfangDerListe = new a();
        anfangDerListe->ersterEintrag("0");
        // Weitere anhaengen - hier bekomme ich die Fehlermeldung
        // von 2) oben
        endeDerListe = endeDerListe->listeErweitern("1", endeDerListe);
        endeDerListe = endeDerListe->listeErweitern("2", endeDerListe);
        endeDerListe = endeDerListe->listeErweitern("3", endeDerListe);
        endeDerListe = endeDerListe->listeErweitern("4", endeDerListe);
       
        // Liste ausgeben (hier sollte "anfangDerListe" nach wie vor auf
        // den Anfang der Liste zeigen)
        anfangDerListe->listeAusgeben();
    
        // Liste abbauen und freigeben
        anfangDerListe->listeLoeschen();
        delete(anfangDerListe);
    }
    

    Ich weiß auch den Rat zu schätzen, es über eine separate Klasse zu machen (würde ich auch gerne) aber das ist hier leider nicht gefordert - hier muss ich es bei der Klasse behalten und lediglich über den Zeiger, der auf das letzte Element gesetzt ist, neue Elemente einfügen können. Vielen Dank für eure Hilfe und Geduld! 🙂. Ich habe das Konzept zwar einigermaßen verstanden, aber fürchte ich kann es nicht vernünftig umsetzen.



  • @Masch sagte in Zeiger auf Ende einer Objektkette setzen:

    a* listeErweitern(string inhalt, a* endeDerListe)

    sollte auch so heissen:

    a* a::listeErweitern(string inhalt, a* endeDerListe)
    

    Dann können deine Variblen wieder private sein und alles ist gut, aber ich dachte das wüsstest du, wobei der erste fehler schon alles sagt.



  • @Masch sagte in Zeiger auf Ende einer Objektkette setzen:

    main.cpp:150:18: error: 'naechsterEintrag' is a private member of 'listenelement'

    Dann mach es doch einfach public:

    class a {
    public:
        string aInhalt;
        a* naechsterEintrag;
    public:
        void ersterEintrag(string inhalt);
        a* listeErweitern(string inhalt, a* endeDerListe);
        void listeAusgeben();
        void listeLoeschen();
    };
    

    Dein Design ist doch eh verhunzt, da kommt es auf ein public mehr oder weniger nicht mehr darauf an.



  • @Quiche-Lorraine sagte in Zeiger auf Ende einer Objektkette setzen:

    Dann mach es doch einfach public

    Wenn du das Problem nicht verstanden hast halte dich mit solchen unsinngen Empfehlungen zurück!



  • @Masch sagte in Zeiger auf Ende einer Objektkette setzen:

    a* listeErweitern(string inhalt, a* endeDerListe) {
    

    ist nicht die Memberfunktion, sondern eine weitere freie Funktion.

    a* a::listeErweitern(string inhalt, a* endeDerListe) {
    

    definiert eine Memberfunktion. Der zweite Parameter wird damit überflüssig und kann weg!



  • Da, das ist Copy&Paste:

    1>------ Build started: Project: codefun, Configuration: Debug x64 ------
    1>codefun.cpp
    1>C:\Users\Swordfish\source\repos\cpp-playground\codefun\codefun.cpp(21,5): error C2065: 'naechsterEintrag': undeclared identifier
    1>C:\Users\Swordfish\source\repos\cpp-playground\codefun\codefun.cpp(23,21): error C2248: 'a::naechsterEintrag': cannot access private member declared in class 'a'
    1>C:\Users\Swordfish\source\repos\cpp-playground\codefun\codefun.cpp(6): message : see declaration of 'a::naechsterEintrag'
    1>C:\Users\Swordfish\source\repos\cpp-playground\codefun\codefun.cpp(4): message : see declaration of 'a'
    1>C:\Users\Swordfish\source\repos\cpp-playground\codefun\codefun.cpp(23,42): error C2065: 'naechsterEintrag': undeclared identifier
    1>C:\Users\Swordfish\source\repos\cpp-playground\codefun\codefun.cpp(24,5): error C2065: 'naechsterEintrag': undeclared identifier
    1>C:\Users\Swordfish\source\repos\cpp-playground\codefun\codefun.cpp(25,5): error C2065: 'naechsterEintrag': undeclared identifier
    1>Done building project "codefun.vcxproj" -- FAILED.
    

Anmelden zum Antworten