std::shared_ptr und dynamische Arrays



  • Morgen,

    nur mal kurze Frage, auf die ich gerade keine Antwort finde und ich möchte mich nicht unbedingt mit dem kryptischen G++-Quellcode rumschlagen, um sie herauszufinden.

    Wenn ich einen std::shared_ptr wiefolgt initialisiere:

    std::shared_ptr<T> values_{new T[dim]};
    

    nutzt die Klasse dann automatisch delete[] oder brauche ich einen benutzerdefinierten Deleter?

    Danke im Voraus,
    Ki

    P.S.: Danke für den automatischen Hinweis, dass Voraus mit nur einem r geschrieben wird 😛



  • Diese Smartpointer funktionieren nur mit new, da sie eben auch nur delete aufrufen. Alles andere funktioniert zwar, aber erzeugt ein Speicherleak, was die Smartpointer ja eigentlich verhindern sollen.

    Letztendlich stellt sich die Frage aber fast gar nicht, weil es dafür Container wie std::vector gibt:

    std::vector<T> values_(dim);
    


  • kjhkhjk schrieb:

    Letztendlich stellt sich die Frage aber fast gar nicht, weil es dafür Container wie std::vector gibt:

    std::vector<T> values_(dim);
    

    Eigentlich wollte ich ja gerade NICHT auf std::vector zurückgreifen. 😃
    Aber ich denke, das überlege ich mir noch einmal, danke für die schnelle Antwort.



  • Ki schrieb:

    Eigentlich wollte ich ja gerade NICHT auf std::vector zurückgreifen. 😃

    wieso? hast du einen grund dafür? zu langsam? zu kompliziert? bugs? großvater wurde von vector erschlagen?


  • Mod

    Ki schrieb:

    kjhkhjk schrieb:

    Letztendlich stellt sich die Frage aber fast gar nicht, weil es dafür Container wie std::vector gibt:

    std::vector<T> values_(dim);
    

    Eigentlich wollte ich ja gerade NICHT auf std::vector zurückgreifen. 😃

    Nun, du könntest, wie von dir selbst vorgeschlagen, einen custom-deleter benutzen. Aber was hättest du dann, außer einem selbst-programmierten vector, aber ohne die ganzen Komfortfunktionen und ohne die Dynamik, die der normale vector bietet? Und das bloß, um einen einzigen Pointer zu sparen. Klingt nach einem sehr schlechten Tausch.


  • Mod

    Ki schrieb:

    nutzt die Klasse dann automatisch delete[]

    nein.

    Ki schrieb:

    brauche ich einen benutzerdefinierten Deleter?

    ja, wobei der Standard schon einen passenden in Form von

    std::default_deleter<T[]>
    

    hat.

    unique_ptr hat eine Spezialsierung für arrays:

    std::unique_ptr<T[]>
    

    die neben dem passenden Deleter auch auch über einen entsprechend überladenen []-Operator verfügt.



  • In den boost Bibliotheken gibt es shared_array , hat es das nicht in den C++11 Standard geschafft? Wenn du den Kopieroverhead von vector vermeiden willst kannst du ihn in einen shared_ptr verpacken.



  • Manchmal moechte man std::vector nicht benutzen, weil bei der Konstruktion die Elemente initialisiert werden. std::vector ist eben kein roher Speicher.


  • Mod

    DocShoe schrieb:

    Wenn du den Kopieroverhead von vector vermeiden willst kannst du ihn in einen shared_ptr verpacken.

    Oder keine unnötigen Kopien durchführen? Lieber lernen, richtig mit der Sprache umgehen, als eine zusätzliche, unnötige Indirektion einzuführen, um vermeidbare Fehler auszuschließen. C++ ist kein Ponyhof, daher reiten die Programme auch so schnell, solange man ihnen nicht künstlich Gewichte anhängt*.

    *: Mein Beitrag zur viel zu weit getriebene Metapher des Jahres 🙂 .


  • Mod

    knivil schrieb:

    Manchmal moechte man std::vector nicht benutzen, weil bei der Konstruktion die Elemente initialisiert werden. std::vector ist eben kein roher Speicher.

    new auch nicht.

    Für rohen Speicher gibt es die Funktionen aus dem memory-Header.



  • Okay, dann formuliere ich es als Frage: Wird bei std::vector<int>(k) jedes Element auf 0 gesetzt? Passiert das auch bei new int[k]? Was passiert bei char? Und was muss ich tun, um rohen Speicher zu erhalten (im Gegensatz zu new char[...])?

    Wenn man nach Usecases sucht, dann findet man nur http://stackoverflow.com/questions/3264299/why-do-i-need-stdget-temporary-buffer



  • insistent schrieb:

    Ki schrieb:

    Eigentlich wollte ich ja gerade NICHT auf std::vector zurückgreifen. 😃

    wieso? hast du einen grund dafür? zu langsam? zu kompliziert? bugs? großvater wurde von vector erschlagen?

    Ach, nur eine Geschmacksfrage. Ich implementiere gerade eine Klasse für mathematische Vektoren dynamischer Größe und fand es für mich angenehmer, die Daten selbst in Form einer Array-List zu implementieren. Was im Endeffekt auf dasselbe hinauskommt, aber ich finde es, wie gesagt, angenehmer.

    camper schrieb:

    Ki schrieb:

    nutzt die Klasse dann automatisch delete[]

    nein.

    Ki schrieb:

    brauche ich einen benutzerdefinierten Deleter?

    ja, wobei der Standard schon einen passenden in Form von

    std::default_deleter<T[]>
    

    hat.

    unique_ptr hat eine Spezialsierung für arrays:

    std::unique_ptr<T[]>
    

    die neben dem passenden Deleter auch auch über einen entsprechend überladenen []-Operator verfügt.

    Danke 🙂 std::default_deleter<T[]> hatte ich ganz vergessen



  • Doch noch einmal ein paar weiterführende Fragen. Ich hoffe, es ist ok, den Thread einfach weiter zu nutzen, anstatt einen neuen zu öffnen.
    Ich denke, es ist doch besser, std::vector direkt zu nutzen, bevor ich es komplett selbst reimplementiere, zumal ich ohnehin schon viel auf die STD-Library zugreife.

    Nun wäre es eine Möglichkeit, das gesagte std::vector -Interface mittels inline Funktionen nachzutippen und alles durchzureichen, was im Prinzip nur langweilige Tipparbeit ist. Nun ist es ja aber so, dass Inline nicht immer vom Compiler inlined wird. Die Frage wäre, ob der Overhead durch Funktionsaufrufe in dem Fall signifikant wäre oder ob ich mich einfach auf den Compiler verlassen kann.

    Eine weitere Möglichkeit wäre Vererbung und das Hinzufügen arithmetischer Funktionen.
    Ist std::vector denn sicher zum öffentlichen Erben?

    MfG, Ki



  • Ki schrieb:

    Ist std::vector denn sicher zum öffentlichen Erben?

    Nur wenn du keine Polymorphie brauchst. Die Destruktoren sind nicht virtual.



  • out schrieb:

    Ki schrieb:

    Ist std::vector denn sicher zum öffentlichen Erben?

    Nur wenn du keine Polymorphie brauchst. Die Destruktoren sind nicht virtual.

    Hatte ich befürchtet 😕
    Aber das heißt, wenn ich keine virtuellen Methoden habe, macht ein nicht-virtueller Destruktor in der Basisklasse gar nichts? Wusste ich auch noch nicht.


  • Mod

    Ki schrieb:

    Aber das heißt, wenn ich keine virtuellen Methoden habe, macht ein nicht-virtueller Destruktor in der Basisklasse gar nichts? Wusste ich auch noch nicht.

    😕 Ich glaube, du hast da was falsch verstanden.

    class foo{};
    class bar:public foo{};
    
    foo* f = new bar;
    delete f;  // Ohje! Der bar-Teil wurde nicht zerstört.
    
    class foo{virtual int i(){}};
    class bar:public foo{};
    
    foo* f = new bar;
    delete f;  // Ohje! Der bar-Teil wurde nicht zerstört.
    
    class foo{virtual ~foo(){}};
    class bar:public foo{virtual ~bar(){}};
    
    foo* f = new bar;
    delete f;  // Passt
    
    class foo{virtual ~foo(){} virtual int i(){}};
    class bar:public foo{virtual ~bar(){}};
    
    foo* f = new bar;
    delete f;  // Passt
    

    Jetzt sind die Beispiele 1 und 3 aber eher hypothetisch, da besitzende Basisklassenzeiger allerhöchstens dann Sinn machen, wenn man damit Polymorphie machen möchte. Beispiel 2 ist das, vor dem du gewarnt wurdest. Beispiel 4 ist, wie es richtig geht. Ob die erste virtuelle Methode in foo, in bar oder einer späteren Klasse auftaucht, ist für die Aussage des Beispiels egal.



  • Ok, ich habe das bisher nicht weiter hinterfragt und generell Destruktoren virtuell (und noexcept) gemacht. 😃 Und normalerweise erbe ich auch nie von STL-Typen, daher die Frage.

    Also wenn keine virtuellen Funktionen in der Klassenhierarchie auftauchen, ist es egal, ob die Destruktoren virtuell sind?


  • Mod

    Ki schrieb:

    Also wenn keine virtuellen Funktionen in der Klassenhierarchie auftauchen, ist es egal, ob die Destruktoren virtuell sind?

    Du brauchst einen virtuellen Destruktor, wenn du ein Objekt über einen Basisklassenzeiger zerstören möchtest. Was allerhöchstens dann Sinn macht, wenn man virtuelle Funktionen in der Hierarchie hat. Denn wieso sollte man sonst jemals einen besitzenden Basisklassenzeiger haben?



  • SeppJ schrieb:

    Ki schrieb:

    Also wenn keine virtuellen Funktionen in der Klassenhierarchie auftauchen, ist es egal, ob die Destruktoren virtuell sind?

    Du brauchst einen virtuellen Destruktor, wenn du ein Objekt über einen Basisklassenzeiger zerstören möchtest. Was allerhöchstens dann Sinn macht, wenn man virtuelle Funktionen in der Hierarchie hat. Denn wieso sollte man sonst jemals einen besitzenden Basisklassenzeiger haben?

    Ahhhh, jetzt sehe ich das Problem erst! 🙄
    Gut, dass ich hier keine Basisklassenzeiger verwenden will, ist klar und man kann es dokumientieren. Aber gibt es auch Programmiertechnische Maßnahmen, um zu verhindern, dass Basisklassenzeiger überhaupt angelegt werden können?


  • Mod

    Ki schrieb:

    Aber gibt es auch Programmiertechnische Maßnahmen, um zu verhindern, dass Basisklassenzeiger überhaupt angelegt werden können?

    private Vererbung


Log in to reply