Zeiger auf Ende einer Objektkette setzen



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


  • Ahh nice, danke @CTecS und @manni66 - wusste nicht, dass ich das trotzdem mit a:: deklarieren muss, aber ergibt Sinn ...

    Jetzt habe ich das Private/Public Problem nicht mehr und es scheint zu laufen. Das einzige Problem ist nur, dass bei der Ausgabe nun nur das letzte Element erscheint und nicht die ganzen anderen, die ich vorher angehängt habe.

    Zwei Möglichkeiten mit denen ich experimentiere grade:

    1. in "int main();"(edited)
    // Bekomme die Meldung, dass "endeDerListe" hier nicht initialisiert ist
    // lege also fest:
        // Erster Eintrag
        anfangDerListe = new a();
        anfangDerListe->ersterEintrag("0");
        endeDerListe = anfangDerListe; //hier versucht, den Zeiger zu kopieren
        // 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);
    
    // Das führt dazu, dass beide Zeiger die gleiche Adresse haben, somit
    // behaelt "anfangDerListe" nicht seinen Wert und übernimmt stets den
    // von "endeDerListe"
    
    1. endeDerListe nicht festlegen nach dem initialisieren, was mir leider gar keine Ausgaben liefert

    Habt ihr Ideen, wie ich "endeDerListe" vernünftig initialisieren / festlegen kann, damit nur "endeDerListe" weiterwandert und "anfangDerListe" auf dem ersten Element bleibt?

    Vielen Dank nochmal für eure Hilfe, hat mir bisher schon viel geholfen. Lieber Gruß.



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

    , endeDerListe)

    Warum existiert das noch?



  • @manni66 - ich hatte versucht den Parameter rauszunehmen, dann hatte ich aber ein Problem mit dem Return Value von "listeErweitern()". Wenn ich das Listenende nicht in den Parameter reinschleuse, was kann "listeErweitern()" dann zurückgeben um es "endeDerListe" in main zuzweisen?



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

    was kann "listeErweitern()" dann zurückgeben um es "endeDerListe" in main zuzweisen?

    naechsterEintrag, wie von @Schlangenmensch schon vor vier Stunden gezeigt?


Anmelden zum Antworten