Smart Pointer statt 'new' und 'delete'?



  • kein new und delete mehr verwendet werden sollte

    Daumeregel: ja! Aber manchmal kommt es drauf an ... . Ich hoffe du hast verstanden, warum. Denn eine Regel zu befolgen, ohne zu verstehen ist wesentlich schlimmer. new wirst du aber im Zusammenhang mit smart pointer trotzdem noch benutzen.

    Ich finde http://klmr.me/slides/modern-cpp.pdf viel zu plakativ. Ich nutze Zeiger weiterhin sehr oft.



  • wenn du mehrere elemente willst: container wie std::vector, std::list, std::forward_list, std::deque, std::map (inkl. unordered- / multi-varianten) oder std::set (ebenfalls inkl. unordered- / multi-varianten).

    Naja aber das allein reicht auch nur dann aus wenn man die Objekte selber in dem Container speichert. Das ist allerdings nicht immer erwünscht, wenn man nur einen Zeiger speichern will braucht man auch hier unique_ptr/shared_ptr.



  • DarkShadow44 schrieb:

    wenn du mehrere elemente willst: container wie std::vector, std::list, std::forward_list, std::deque, std::map (inkl. unordered- / multi-varianten) oder std::set (ebenfalls inkl. unordered- / multi-varianten).

    Naja aber das allein reicht auch nur dann aus wenn man die Objekte selber in dem Container speichert. Das ist allerdings nicht immer erwünscht, wenn man nur einen Zeiger speichern will braucht man auch hier unique_ptr/shared_ptr.

    Immer wenn ich einen smartpointer in einem Container sehe, dann graust es mir.
    Wir haben boost pointer container dafür.



  • DarkShadow44 schrieb:

    wenn du mehrere elemente willst: container wie std::vector, std::list, std::forward_list, std::deque, std::map (inkl. unordered- / multi-varianten) oder std::set (ebenfalls inkl. unordered- / multi-varianten).

    Naja aber das allein reicht auch nur dann aus wenn man die Objekte selber in dem Container speichert. Das ist allerdings nicht immer erwünscht, wenn man nur einen Zeiger speichern will braucht man auch hier unique_ptr/shared_ptr.

    dann nimmt man ptr_vector aus boost.



  • Habe vor einigen Tagen gelesen, dass man in C++11 kein new und delete mehr verwendet werden sollte

    1. die Thematik betrifft nicht nur c++ ab C++11, sondern war auch vorher schon aktuell
    2. Formulierung 🙂 man sollte kein "rohes" new und delete verwenden. Auch in verbindung mit smartpointer kommst um new nicht drumherum, und das delete wird nur automatisch gecallt.

    Wenn man keinen std::vector verwenden möchte, wie allokiere ich dynamisch Speicher via Smart Pointer anstatt mit new und delete?

    Frage: warum kein std::vector ? der bietet sich doch förmlich an, wenn man Speicher für einen zusammenhängenden bereich von gleichartigen Daten allokieren muss ?

    smartpointer sind eher für einzelne Objecte aufn Heap, als für arrays gedacht.

    Allerdings soll
    std::unique_ptr<int[]> myptr(new int[9]);
    so ueberladen, sein, das er correct das delete[] statt dem normalen delete aufruft.
    Aber ehrlich, warum sollt man das so machen?
    was wäre der Vorteil gegenüber std::vector<int> myArray(9,0); ???

    Ciao ...



  • RHBaum schrieb:

    Allerdings soll
    std::unique_ptr<int[]> myptr(new int[9]);
    so ueberladen, sein, das er correct das delete[] statt dem normalen delete aufruft.
    Aber ehrlich, warum sollt man das so machen?
    was wäre der Vorteil gegenüber std::vector<int> myArray(9,0); ???

    mir fällt da nur ein dass std::vector in der regel 2 * sizeof(std::size_t) mehr speicher braucht (für size und reserve ) und in vielen implementierungen in zweierpotenzen alloziert. diese beiden eigenschaften *könnten* in randfällen unerwünscht sein (mir fällt keiner ein im moment). aber im allgemeinen hast du recht.



  • asfdlol schrieb:

    RHBaum schrieb:

    Allerdings soll
    std::unique_ptr<int[]> myptr(new int[9]);
    so ueberladen, sein, das er correct das delete[] statt dem normalen delete aufruft.
    Aber ehrlich, warum sollt man das so machen?
    was wäre der Vorteil gegenüber std::vector<int> myArray(9,0); ???

    mir fällt da nur ein dass std::vector in der regel 2 * sizeof(std::size_t) mehr speicher braucht (für size und reserve ) und in vielen implementierungen in zweierpotenzen alloziert. diese beiden eigenschaften *könnten* in randfällen unerwünscht sein (mir fällt keiner ein im moment). aber im allgemeinen hast du recht.

    Wobei das ja so ist, das die Allokation in zweierpotenzen nur dann zum tragen kommt, wenn man vorher nicht weiß, wie viele Elemente man einfügen möchte. In diesen Fällen hilft einem aber das new int[9] auch nichts und zur Not kann man immer noch ein shrink_to_fit machen.



  • Immer wenn ich einen smartpointer in einem Container sehe, dann graust es mir.
    Wir haben boost pointer container dafür.

    Und wenn man nun aber keine Abhängigkeit von boost will ? 🙄



  • DarkShadow44 schrieb:

    Immer wenn ich einen smartpointer in einem Container sehe, dann graust es mir.
    Wir haben boost pointer container dafür.

    Und wenn man nun aber keine Abhängigkeit von boost will ? 🙄

    Das ist einfach nur eine billige Ausrede um es falsch zu machen.
    Wir haben auch Smartpointer lange vorher verwendet bevor es sie im Standard gab.

    Der korrekte Weg Zeiger in einem container zu speichern sind pointer container, ob diese jetzt von boost kommen, selber geschrieben sind oder aus dem Ausland importiert wurden ist dabei egal.

    Never ever einen Smartpointer in einen Container packen - das ist nur Faulheit.

    Es gibt Situationen wo man einen shared_ptr in einem Container haben will (sehr sehr wenige) aber es gibt NIE eine Situation wo man einen unique_ptr haben will.



  • asfdlol schrieb:

    mir fällt da nur ein dass std::vector in der regel 2 * sizeof(std::size_t) mehr speicher braucht (für size und reserve ) und in vielen implementierungen in zweierpotenzen alloziert. diese beiden eigenschaften *könnten* in randfällen unerwünscht sein (mir fällt keiner ein im moment). aber im allgemeinen hast du recht.

    Selbst das bekommst du hin, wenn du vor jedem push_back size() gegen capacity() prüfst und ggf. mit resize() hinten Platz für neue Elemente reservierst.

    Was mich bei dieser stereotypen Predigt immer stört ist, dass der Kontext nicht berücksichtigt wird. Sobald man Objekthierachien verwalten muss, bei denen Objekte über Zeiger in Beziehung stehen funktioniert ein besitzender vector ohne (smart_) pointer einfach nicht mehr. Aber irgendwie wird hier immer davon ausgegangen, dass man einfach nur eine Menge von Objekten verwalten muss, die mit nichts in Relation stehen.



  • Shade Of Mine schrieb:

    Der korrekte Weg Zeiger in einem container zu speichern sind pointer container, ob diese jetzt von boost kommen, selber geschrieben sind oder aus dem Ausland importiert wurden ist dabei egal.

    Du willst tatsächlich sagen, dass es besser ist einen vector von Pointern von grund auf neu zu implementieren, anstatt einen stl:vector mit smartpointern zu nehmen?



  • Shade Of Mine schrieb:

    Es gibt Situationen wo man einen shared_ptr in einem Container haben will (sehr sehr wenige) aber es gibt NIE eine Situation wo man einen unique_ptr haben will.

    Kannst du das bitte etwas näher erläutern?



  • Zeiger speichern will braucht man auch hier unique_ptr/shared_ptr.

    Wenn ich Zeiger speichern will, dann nutze ich Zeiger.

    Was mich an std::vector manchmal stoert, ist: er initialisiert den Speicher bzw. ruft den Konstruktor auf. Bei primitiven Datentypen/PODs manchmal unerwuenscht.



  • @TNA:
    Ja, die halbe Stunde arbeit wirst du schon hinbekommen. Man kann das ja auch als container adapter implementieren. Ist ja keine Hexerei. Echt nicht.

    @Skym0sh0:
    unique_ptr in einem Container bedeutet, dass man einen pointer container haben will - da der Container der Besitzer ist. Das erspart eine Menge Verwaltungsaufwand es einfach den Container anzuschaffen - denn der weiß ja alles was er wissen muss. Er kann dann auch eine viel bequemere Syntax anbieten.

    shared_ptr dagegen können theoretisch Sinn machen, da diese ja Aussagen "ich weiß nicht wer der Besitzer ist". So könnte es sein, dass man einfach solche Objekte in einen Container packen will und wieder entfernen - die Lebenszeit des Objektes aber nicht direkt mit dem Container gekoppelt ist.

    Das kommt sehr selten vor - ich denke dass shared_ptr generell sehr sehr sehr selten verwendet werden sollte, denn wie gesagt: shared_ptr heisst: Ich weiß nicht bzw. es interessiert mich nicht wer der Besitzer ist. Das sollte die Ausnahme und nicht die Regel sein. Da Besitzrechte klar geklärt gehören.



  • Shade Of Mine schrieb:

    @TNA:
    Ja, die halbe Stunde arbeit wirst du schon hinbekommen.

    Dein Vertrauen ehrt mich, aber ich würde mir nie zutrauen, in einer halben Stunde Code in "STL-Qualität" zu erstellen.

    Shade Of Mine schrieb:

    Man kann das ja auch als container adapter implementieren. Ist ja keine Hexerei. Echt nicht.

    Das hört sich eher nach einer brauchbaren Lösung an.



  • Shade Of Mine schrieb:

    @Skym0sh0:
    unique_ptr in einem Container bedeutet, dass man einen pointer container haben will - da der Container der Besitzer ist. Das erspart eine Menge Verwaltungsaufwand es einfach den Container anzuschaffen - denn der weiß ja alles was er wissen muss.

    Wieso?
    Meistens will ich doch einen PointerContainer, um Polymorphie zu nutzen. Ich stecke viele unterschiedliche Objekte mit gleichem Interface rein. Klar, der Container ist der Besitzer, aber ein unique_ptr im Container würde es doch genauso tun.
    (Überzeug mich!)

    Shade Of Mine schrieb:

    shared_ptr dagegen können theoretisch Sinn machen, da diese ja Aussagen "ich weiß nicht wer der Besitzer ist". So könnte es sein, dass man einfach solche Objekte in einen Container packen will und wieder entfernen - die Lebenszeit des Objektes aber nicht direkt mit dem Container gekoppelt ist.

    Das kommt sehr selten vor - ich denke dass shared_ptr generell sehr sehr sehr selten verwendet werden sollte, denn wie gesagt: shared_ptr heisst: Ich weiß nicht bzw. es interessiert mich nicht wer der Besitzer ist. Das sollte die Ausnahme und nicht die Regel sein. Da Besitzrechte klar geklärt gehören.

    Keine Frage. Dem ist so!



  • Skym0sh0 schrieb:

    Wieso?
    Meistens will ich doch einen PointerContainer, um Polymorphie zu nutzen. Ich stecke viele unterschiedliche Objekte mit gleichem Interface rein. Klar, der Container ist der Besitzer, aber ein unique_ptr im Container würde es doch genauso tun.
    (Überzeug mich!)

    Ich muss Niemanden überzeugen, ich sage dir einfach was besser ist. Du wirst es schon irgendwann einsehen.

    Aber gängige Argumente sind zB Performance und Klarheit des Codes.
    unique_ptr ist langsamer als ein ptr container und das dauernde dereferenzieren im Client Code ist mühsam und unschön.

    Der Grund warum man einen unique_ptr in einen Container packt, ist Faulheit. Wenn wir ptr container in der Standard Library hätten, dann hätten wir diese Diskussion hier nicht.

    Prinzipiell ist das halt eine Sache der Einfachheit: man sieht sich an was automatisch geht und wenn man kein boost hat ist unique_ptr in Container besser als roher Zeiger in Container - so entsteht sowas halt. unique_ptr ist zB eh schon viel besser als früher wo die Leute immer shared_ptr in den Container gepackt haben. Aber es ist halt eine Notlösung mangels vorhandenen ptr container.

    @TNA:
    Dann mach einen Container Adapter - das ist trivial, du musst nur die erase Operationen genauer ansehen, insert kann man 1:1 weiter leiten. Und die Iteratoren sind auch relativ trivial, die Vergleichsoperatoren sind halt etwas schreib arbeit.

    Ich meine das ernst, das ist nicht kompliziert.



  • Shade Of Mine schrieb:

    Aber gängige Argumente sind zB Performance und Klarheit des Codes.
    unique_ptr ist langsamer als ein ptr container und das dauernde dereferenzieren im Client Code ist mühsam und unschön.

    Hast du ein Beispiel wo ein boost::ptr_vector signifikant schneller ist als ein std::vectorstd::unique\_ptr? Der ptr_vector ist ja tief innendrin auch bloß ein std::vector<void*>. Und wenn ich die Implementierung für den boost::ptr_sequence_adapter so überfliege, würde ich eigentlich erwarten, dass der Overhead beider Lösungen in etwa aufs gleiche rauslaufen müsste.

    Der große Vorteil den ich beim unique_ptr sehe ist dass es einfacher ist ein Element aus dem container rauszunehmen und die Ownership mitzunehmen.



  • zB beim realloc des vectors.

    Alleine dass ein unique_ptr einen deleter mitführt macht ihn langsamer. Bei unique_ptr wird jedes Element des Containers für sich einzeln behandelt - bei einem ptr Container wird der ganze Inhalt gleich behandelt.

    Bei unique_ptr ist der Geschwindigkeitsunterschied natürlich nicht die Welt - bei shared_ptr ist das viel schlimmer, aber es ist unnötiger Overhead der ja keinen Mehrvorteil bringt sondern den Code schwerer zu verstehen macht.



  • Artikel zum Thema RAII: Link


Anmelden zum Antworten