std::shared_ptr und dynamische Arrays



  • 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



  • camper schrieb:

    Ki schrieb:

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

    private Vererbung

    Dann könnte ich auch gleich wieder eine Aggregation nehmen und alle Methoden durchreichen.


  • Mod

    Ki schrieb:

    camper schrieb:

    Ki schrieb:

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

    private Vererbung

    Dann könnte ich auch gleich wieder eine Aggregation nehmen und alle Methoden durchreichen.

    oder einfach using-Deklarationen verwenden



  • camper schrieb:

    Ki schrieb:

    camper schrieb:

    Ki schrieb:

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

    private Vererbung

    Dann könnte ich auch gleich wieder eine Aggregation nehmen und alle Methoden durchreichen.

    oder einfach using-Deklarationen verwenden

    Oh 😃 Gut, DAS ist ein toller Einwand, danke 🙂



  • knivil schrieb:

    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[...])?

    In allen Fällen wird der Skalar nicht initialisiert, denn alle Elemente werden default-initialized , was bei Skalaren bedeutet dass nichts geschieht. Genauso bekommst du auch rohen Speicher. Denn wenn du ein Array von Skalaren hast, dann werden diese (solange du keinen Initializer im Deklarator hast) default-initialized.
    (Willst du hingegen, dass jedes Element value-initialized wird, dann schreibst du new char[...]() , siehe auch §8.5/11).

    Im Standard ist das ganze folgendermaßen zu deduzierenn.

    N3690 §8.5/11 schrieb:

    If no initializer is specified for an object, the object is default-initialized.

    Für Arrays heißt das:

    N3690 §8.5/7 schrieb:

    To default-initialize an object of type T means:
    — if T is an array type, each element is default-initialized;

    Und für Skalare heißt das:

    To default-initialize an object of type T means:
    — otherwise [(Die beiden anderen Stichpunkte sind für Klassen und Arrays)], no initialization is performed.

    Bei vector geht das eine ganze Weile so, bis man wieder bei new ankommt. Da bei default-insertion keine Parameter an std::allocator::construct mitgegeben werden, wird es value-initialized*.

    *Dank der Syntax die der Standard für den Aufruf von new festlegt.



  • Sone schrieb:

    Bei vector geht das eine ganze Weile so, bis man wieder bei new ankommt. Da bei default-insertion keine Parameter an std::allocator::construct mitgegeben werden, wird es default-initialized.

    Knapp dabei ist auch daneben.



  • vaterundsohn schrieb:

    Sone schrieb:

    Bei vector geht das eine ganze Weile so, bis man wieder bei new ankommt. Da bei default-insertion keine Parameter an std::allocator::construct mitgegeben werden, wird es default-initialized.

    Knapp dabei ist auch daneben.

    ::new ((void*)c) C(forward<Args>(args)...)
    

    Oh, du hast Recht. Die Klammer bleibt bestehen... mein Fehler. Dann ist es value-initialization, und es wird zero-initialized (da Skalar)...



  • Also wird bei std::vector zero-initialized und bei new default-initialized?



  • knivil schrieb:

    Also wird bei std::vector zero-initialized und bei new default-initialized?

    Bei vector<int> ja. Allgemein wird bei vector value-initialized (was bei ints zero bedeutet) und bei new default-initialized.

    Das ist aber kein Grund new statt vector zu verwenden, vector.reserve() hat mir bis jetzt immer ausgereicht.*

    * Gut, letztens hatte ich mir eine memchunk-Klasse geschrieben, die mir einige Gigabyte virtuellen Memory allokiert, der dann von ihr verwaltet wird. In dem Fall habe ich auf new + std::unique_ptr<char*> + Verwaltungsdaten zurückgegriffen, weil ein vector falsche Semantik gehabt hätte.



  • Siehste ... und wenn ich Bilddaten verarbeite, dann moechte ich mir auch gern die Initialisierung sparen, da sowieso sofort was anderes reingeschrieben wird. Normalerweise brauche ich auch kein push_back, reserve, ...



  • knivil schrieb:

    Siehste ... und wenn ich Bilddaten verarbeite, dann moechte ich mir auch gern die Initialisierung sparen, da sowieso sofort was anderes reingeschrieben wird. Normalerweise brauche ich auch kein push_back, reserve, ...

    Bei Bilddaten wäre vector+reserve+push_back aber angebracht, weil dann hast du gleich Move- und Kopiersemantik.

    Meine Klasse hat garantiert, dass ein Pointer nie invalidiert wird, das braucht man bei Bildern eher nicht.


Anmelden zum Antworten