Hängende Zeiger vermeiden



  • @Belli sagte in Hängende Zeiger vermeiden:

    @chris4cpp sagte in Hängende Zeiger vermeiden:

    void doIt(Objekt A); // teuer call by value
    void doIt(Objekt* A); // billig da nur ein Zeiger übergeben wird call by reference
    void doIt(int A); // billig 
    void doIt(int* A); // genauso billig
    

    Was ist daran Quatsch?

    Das ist hier was ganz anderes, als zu Beginn des Threads. Hier geht es um den Informationsaustausch zwischen Funktionen, ursprünglich ging es um Datenhaltung in einem Objekt Deiner Klasse.
    Hier, bei Herumreichen zwischen Funktionen würde man eine Referenz einem Zeiger vorziehen.

    Oder wenn ein Objekt den Besitzer wechseln soll, würde man mit move-assignment arbeiten.
    Zeiger braucht man eigentlich gar nicht mehr. Mir fällt jetzt akut auch kein Fall ein, wo das unbedingt nötig wäre.



  • @chris4cpp sagte in Hängende Zeiger vermeiden:

    Die Frage von manni hatte mich verwirrt und in eine völlig andere Richtung gebracht. Aber die Situation aufzuklären war anscheinend nicht sein Ziel, das hat mich sehr geärgert.

    Fragen von manni66 dienen praktisch immer dazu, dass derjeniger, der gefragt wurde, einmach mal ordentlich über das gesagte bzw. gefragte nachdenken soll. Nimm Mannis Fragen/Aussagen ernst!



  • @It0101 sagte in Hängende Zeiger vermeiden:

    Zeiger braucht man eigentlich gar nicht mehr. Mir fällt jetzt akut auch kein Fall ein, wo das unbedingt nötig wäre.

    Interaktion mit bestimmten APIs, bei denen man unbedingt Zeiger braucht bzw. man auf eine ziemlich tiefe Ebene in die Speicherverwaltung eingreifen muss: d.h. keine Anfängerthemen.



  • Kann man denn wirklich auf Zeiger verzichten? Ich meine bei meiner Audio Lib muss ich schon allein wegen der callback Funktion auch mit Zeigern arbeiten. Gut geschenkt, das ist nur eine WrapperKlasse die ich dafür geschrieben haben.

    Wie sieht es bei der Polymorphie aus, kann man da auf Zeiger verzichten?

    @wob Ja, da muss ich erst mal dahinter kommen wie die Leute so ticken hier. Mit "Willst du mich verarschen" war er zu erst unten durch. So ein Verhalten erwarte ich von einem 17 Jährigen der mal kurz ein C++ YouTube Video nebenbei gesehen hat. Nie wäre ich auf die Idee gekommen dass dies ein Mitglied mit Kompetenz sein könnte. Aber wenn ihr mit das so sagt, dann glaube ich das. Soziale Kompetenz ist aber meiner Meinung nach sehr ausbaufähig. Sei es drum.



  • @chris4cpp

    hier sind zu 99,99% leute, die den ganzen tag am rechner sitzen, sich nur von pizza, cola und schwarzem kaffee ernähren, deren einzige soziale interaktion im annehmen der bestellung vom pizzaboten besteht und die generell eher fach- als sozialkompetenz haben 🤨 . ich habe mir mal sagen lassen, dass man sich im laufe der zeit an solche umgangsformen gewöhnt.

    referenzen sind, vereinfacht gesagt, zeiger, deren wert man nicht ändern kann. wenn du also im speicher herumspringen möchtest (microcontroller oder gerätetreiber z.b.), dann nimm zeiger, wenn du nur eine referenz übergeben möchtest ("call-by-reference"), dann nimm referenzen.



  • @chris4cpp sagte in Hängende Zeiger vermeiden:

    Kann man denn wirklich auf Zeiger verzichten? Ich meine bei meiner Audio Lib muss ich schon allein wegen der callback Funktion auch mit Zeigern arbeiten. Gut geschenkt, das ist nur eine WrapperKlasse die ich dafür geschrieben haben.

    Mit „auf Zeiger verzichten“ meint in diesem Kontext, dass man keine rohen Zeiger mehr nutzt. Es gibt in modernen C++ Mechanismen die das einem ermöglichen. Z.B. bei den SmartPointer wird intern, für Nutzer der Klasse komplett verdeckt, natürlich ein Zeiger genutzt. Entscheidend ist nun, dass man mit diesen modernen Methoden massenweise Programmierfehler eliminieren kann, die so typisch für altes C++ und insbesondere C sind. Deshalb ist so wichtig, dass man weiß wie man es „richtig“ macht, und möglichst den Gebrauch von rohen Zeiger vermeidet.

    Ich gehöre zu den wenigen hier im Forum, die beständig schreiben, dass man in C++ realistischer Weise nicht auf rohe Zeiger auf absehbare Zeit verzichten kann, weil es noch immer APIs gibt, die das erfordern. Wie Du selbst schreibst, bist Du genau auf eine solche API gestoßen. Deshalb bleibt das Thema rohe Zeiger für C++ Neulinge ein Thema. Aber meiner Meinung nach sollte man nicht damit anfangen, sondern zuerst C++ lernen und dann als Fortgeschrittenen Thema rohe Zeiger behandeln. Nicht weil man das Problem mit einer geeigneten Wrapperklasse nicht weg abstrahieren könnte, sondern weil einem Anfänger das Wissen fehlt wie man es richtig macht. Wichtig hierbei Rule of Three bzw. Rule of Five durchlesen und dann vollständig umsetzen.



  • Und es gibt noch den Bereich, wenn man selbst Datenstrukturen mit verketteten Listen oder ähnlichem baut. Wenn man nämlich (einfach) verkettete Listen mit einem unique_ptr baut und die Liste groß wird, ist die rekursive Aufräumstrategie nämlich nicht so eine tolle Idee... Gut, wie oft ist das relevant?

    Außerdem gibt es einige Frameworks, die selbst intern tracken, welche Objekte erzeugt wurden. Wenn man dann ein Eltern-Objekt deleted, deleten die gleich die Kinder (oder was sie für ihre Kinder halten) mit. Bei GUIs ist das ja noch einleuchtend, aber das passiert auch teilweise anderswo. Noch besser, wenn es dann irgendwo versteckte Switches gibt, um das Verhalten an- und auszuschalten. Da kann man sich mit Smartpointern auch in den Fuß schießen. Aber ansonsten: wenn möglich, std::unique_ptr nutzen!



  • @wob
    std::list?

    aber ich habe doch noch einen fall gefunden, wo zeiger unabdingbar sind: nämlich immer dann, wenn man grundlegende datenstrukturen, sortieralgorithmen etc. zum verständnis der internen abläufe selbst programmieren soll.😉



  • @Wade1234 sagte in Hängende Zeiger vermeiden:

    std::list?

    Ich schrieb "wenn man selbst Datenstrukturen (...) baut". List ist außerdem in beide Richtungen iterierbar, also doppelt verkettet (ich hatte oben absichtlich auch noch das "einfach" eingefügt).

    Wozu brauchst du bei einem Sortieralgorithmus Zeiger? std::sort (und eigentlich die gesamte STL) macht es vor - es nimmt einfach 2 Iteratoren.



  • @wob

    also wenn die daten in einem array sind, kann man doch prima mit zeigern arbeiten. ich hab hier irgendwo mal gelesen, dass das schneller geht.


  • Mod

    @Wade1234 sagte in Hängende Zeiger vermeiden:

    @wob

    also wenn die daten in einem array sind, kann man doch prima mit zeigern arbeiten. ich hab hier irgendwo mal gelesen, dass das schneller geht.

    Erklär doch mal, wie Iterator und Zeiger sich zueinander verhalten. Und was ein Iterator in einem Array ist. Oder wie std::sort ein Array sortieren kann.



  • Ich danke euch vielmals für die sehr wertvollen Antworten.

    Ich bin in meinem Projekt jetzt soweit, dass alle rohen Zeiger raus sind: Wenn ich noch wirklich einen Zeiger brauchte kam meist shared_ptr oder schlicht weg Referenzen zum Einsatz. C-arrays wurden zudem durch std::array ersetzt. Von mir selbst gibt es also kein new, delete oder carray mehr.

    Nur meine Audio Third Party Lib hat noch was mit rohen Zeigern drin und halt die Qt Widgets, aber die Qt Lib räumt ja da von selbst auf wenn ein Eltern Element gelöscht wird.

    Bei SmartPointer finde ich die Fehlermeldungen ziemlich strange teilweise, aber meist kommt dann doch auf den Fehler, ansonsten hilft StackOverflow.

    Kann es sein dass die Fehlermeldungen zu schwierig zu lesen sind, weil die SmartPointer mit Templates realisiert sind? An Templates wage ich mich noch überhaupt nicht ran, das sieht alles tierisch kompliziert aus. Es wird zwar immer geschrieben wie elegant man damit Sachen lösen kann, aber mich grault es jetzt schon vor kryptischen Fehlermeldungen.



  • @chris4cpp
    Ja, das ist bei templates leider so. Und wenn man sich damit noch nicht beschäftigt hat steht man wie der Ochs vorm Berg und kann mit der Fehlermeldung so gar nichts anfangen. Das kommt aber mit der Zeit.



  • @chris4cpp sagte in Hängende Zeiger vermeiden:

    Kann es sein dass die Fehlermeldungen zu schwierig zu lesen sind, weil die SmartPointer mit Templates realisiert sind? An Templates wage ich mich noch überhaupt nicht ran, das sieht alles tierisch kompliziert aus. Es wird zwar immer geschrieben wie elegant man damit Sachen lösen kann, aber mich grault es jetzt schon vor kryptischen Fehlermeldungen.

    Das ist ein Dauerbrenner in C++. Vor etlichen Jahren wollte man Concepts einführen, damit die Fehlermeldungen endlich sinnvoller lesbar sind. Concepts sind noch immer kein Teil der Sprache. Man sollte mit Templates selbst schreiben erst dann anfangen, wenn man die Sprache selbst halbwegs beherrscht.


  • Mod

    Du solltest übrigens nicht annehmen, dass du explizite Smartpointer brauchst, nur weil eine API Zeiger verlangt. Viele APIs brauchen Zeiger, aber nur wenige benutzen diese auch zur Ressourcenverwaltung. Oft geht es nur um die Übergabe von Daten im C-Stil.

    Ein bekanntes Beispiel ist die Stringverarbeitungs-API von C selbst. Die kannst du problemlos mit std::vector oder std::string benutzen, ohne irgendwo einen Smartpointer im Programm zu haben. Da lässt du den String/Vector alles intern regeln, und wenn du (aus unerfindlichen Gründen) unbedingt die Schnittstelle für C-Strings nutzen möchtest, dann würdest du einen Zeiger auf deren interne Datenfelder übergeben. Nicht ohne Zufall stellen diese Container eine passende Schnittstelle zur Verfügung:

        #include <vector>
        #include <string>
        #include <cstring>
        using namespace std;
         
        int main() {
        	string foo = "foo\0";
        	vector<char> bar(4);
        	strcpy(bar.data(), foo.data());
        	printf("%s", bar.data());
        }
    

    Das geht natürlich dann nicht mehr, wenn diese APIs selber Ressourcen verwalten und beispielsweise einen Handler rausgeben. Dann muss doch ein Smartpointer her (oder eine Wrapperklasse). Zum Beispiel die Dateizugriffsschnittstelle von C:

          void close_file(std::FILE* fp) { std::fclose(fp); }
    
          // ...
    
          std::unique_ptr<std::FILE, decltype(&close_file)> fp(std::fopen("demo.txt", "r"),
                                                               &close_file);
    

    (Beispiel von https://en.cppreference.com/w/cpp/memory/unique_ptr)



  • Kleine Optimierung:

    struct file_closer {
        void operator()(std::FILE* fp) const {
            std::fclose(fp);
        }
    };
    
    void test() {
        std::unique_ptr<std::FILE, file_closer> fp(std::fopen("demo.txt", "r"));
    }
    


  • @SeppJ sagte in Hängende Zeiger vermeiden:

    	string foo = "foo\0";
    

    Die '\0' ist doppelt gemoppelt und ein vector<char> bar(4) reicht dafür auch nicht.

    edit: Ja, ok, std::strcpy() hört natürlich bei der ersten '\0' auf.

    @chris4cpp Rohe Zeiger per se sind nicht böse. Rohe besitzende Zeiger sind böse.


  • Mod

    @Swordfish sagte in Hängende Zeiger vermeiden:

    Die '\0' ist doppelt gemoppelt und ein vector<char> bar(4) reicht dafür auch nicht.

    edit: Ja, ok, std::strcpy() hört natürlich bei der ersten '\0' auf.

    Auch die erste Aussage ist nicht richtig:
    https://ideone.com/wubSCh

    Tzz, tzz, tzz. Immer diese Kinners, die denken, sie könnten mir was von C-Strings erzählen…
    😛



  • @Swordfish sagte in Hängende Zeiger vermeiden:

    Die '\0' ist doppelt gemoppelt

    Steht und mir ist auch nicht klar warum Du die 0 explizit hinschreiben möchtest.



  • @SeppJ: Hattest du den falschen Code gepostet (mit "foo\n" anstatt "foo\0")?

    -> korrigierter Code

    Ein String kann zwar '\0' speichern, aber der Konstruktor string(const char *) liest nur bis zu diesem Zeichen (exklusiv). @Swordfish hat also Recht mit seiner Aussage. 😉


Anmelden zum Antworten