Verweis uaf eine gelöschte Funktion in std::unique_ptr



  • Ich gebe diesen Vector ja nach außen. Damit ein Anwender damit arbeiten kann bzw. die Daten analysieren kann.
    Wenn der Anweder jedoch folgendes machen würde:

    const std::vector<std::unique_ptr<Abstract>>& items = example.Get();
    
    for(auto& abstract : items ){
       abstract->someFunction();
    }
    
    ...
    
    for(auto& abstract : items ){
       abstract->someFunction();
    }
    

    Würde die zweite Schleife nur noch leere "Hüllen" liefern... Dies finde ich sehr unschön, daher denke ich, dass es so gar keinen Sinn macht.



  • Quaneu schrieb:

    Wenn der Anweder jedoch folgendes machen würde:

    const std::vector<std::unique_ptr<Abstract>>& items = example.Get();
    
    for(auto& abstract : items ){
       abstract->someFunction();
    }
    
    ...
    
    for(auto& abstract : items ){
       abstract->someFunction();
    }
    

    Würde die zweite Schleife nur noch leere "Hüllen" liefern...

    Das ist falsch.

    Dies finde ich sehr unschön, daher denke ich, dass es so gar keinen Sinn macht.

    Wenn dem so wäre, wäre es unschön. Es ist aber nicht so. (hast du es ausprobiert?)



  • Quaneu schrieb:

    Ich gebe diesen Vector ja nach außen. Damit ein Anwender damit arbeiten kann bzw. die Daten analysieren kann.
    Wenn der Anweder jedoch folgendes machen würde:

    const std::vector<std::unique_ptr<Abstract>>& items = example.Get();
    
    for(auto& abstract : items ){
       abstract->someFunction();
    }
    
    ...
    
    for(auto& abstract : items ){
       abstract->someFunction();
    }
    

    Würde die zweite Schleife nur noch leere "Hüllen" liefern... Dies finde ich sehr unschön, daher denke ich, dass es so gar keinen Sinn macht.

    Woher kommt das "wissen", dass die unique_ptrs in items nach der ersten schleife leer sind?
    Hast du das ausprobiert oder vermutest du nur?

    Im deinem Beispiel ist die schleife das gleiche wie

    for(std::unique_ptr<Abstract>& abstract : items ){
       abstract->someFunction();
    }
    

    Da wird nichts gemoved.



  • Entschuldige Du hast recht, das Beispiel war falsch. Hatte es mit items.at() ausprobiert. Und da geht es nicht, wenn man sich zweimal den selben Eintrag holt.



  • Quaneu schrieb:

    Entschuldige Du hast recht, das Beispiel war falsch. Hatte es mit items.at() ausprobiert. Und da geht es nicht, wenn man sich zweimal den selben Eintrag holt.

    Auch das ist Blödsinn.



  • Kommt eben drauf an wie man sich den Eintrag holt.

    auto &a = items.at(0); // Geht zweimal
    auto a = std::move(items.at(0)); // Geht nicht zweimal
    


  • Ich halte es eher so, daß ich versuche, einen unique_ptr gar nicht erst aus meiner Klasse heraus zu geben, außer wenn tatsächlich ein Besitz-Transfer stattfinden soll.

    Die Regel wäre etwa folgende:

    • Der Besitzer des unique_ptr ist ALLEINIGER Besitzer des Objekts
    • Sollen keine anderen Objekte die betreffenden Objekte besitzen dürfen, wird ein raw-pointer zurückgegeben
    • raw-pointer sind weak-pointer, garantieren also keinen Besitz über das betreffende Objekt
    • Sollen andere Objekte ebenfalls Besitz übernehmen können, sollte ein shared_ptr verwendet werden

    Hatte allerdings glaube ich noch nicht den Fall, daß ich eine komplette Objektliste aus unique pointern zurückgeben musste.
    Theoretisch kann man da eine zweite Liste aus raw-pointern in der Klasse speichern und die dann anstatt der unique_ptr Liste zurückgeben, aber arg schön ist das auch nicht. Da würde ich dann eher zu shared pointern tendieren. Wenn dein Code nicht nur über die Liste iteriert, sondern auch mal ein Objekt aus der Liste irgendwo anders abspeichern will wäre das eh die bessere Wahl.



  • Quaneu schrieb:

    Kommt eben drauf an wie man sich den Eintrag holt.

    auto &a = items.at(0); // Geht zweimal
    auto a = std::move(items.at(0)); // Geht nicht zweimal
    

    Wiso willst du das item raus nehmen?
    Wenn es nur benötigt wird um daten aus dem element zu lesen reicht es über die referenz da muss nichts kopiert/gemoved werden



  • Quaneu schrieb:

    Kommt eben drauf an wie man sich den Eintrag holt.

    auto &a = items.at(0); // Geht zweimal
    auto a = std::move(items.at(0)); // Geht nicht zweimal
    

    Unteres kompiliert nicht einmal. Ergo: Blödsinn.

    Grund: du hast aus deiner Funktion eine const-ref zurückgegeben.

    Außerdem: wenn das jetzt nicht const wäre, hättest du ja explizit mit std::move aufgefordert, den Originalwert zu ändern. Dann wärst du selbst schuld.



  • Quaneu schrieb:

    Mittlerweile zweifel ich jedoch grundsätzlich an der Verwendung von unique_ptr in einem Vector, wenn dieser nach außen gegeben wird...

    Einfache Pointer sind da auch nicht unbedingt besser: man kann delete aufrufen oder den Wert ändern.
    Getter/Setter werden sowieso viel zu exzessiv verwendet. Man sollte dann aber wenigstens keinen Zugriff auf die Innereien haben (wie in deinem Fall).
    Wenn du keine Nullpointer in deinem Vector hast, könnte man da was mit boost löten und Iteratoren auf konstante Referenzen statt einem kompletten Vector ausliefern:

    #include <iostream>
    #include <vector>
    #include <memory>
    #include <algorithm>
    #include <boost/iterator/indirect_iterator.hpp>
    
    int main()
    {
      std::vector<std::unique_ptr<int>> v;
      v.emplace_back(new int{1});
      v.emplace_back(new int{2});
    
      boost::indirect_iterator<decltype(v.begin()), const int&> 
        indirect_first(v.begin()), indirect_last(v.end());
    
      std::copy(indirect_first, indirect_last, std::ostream_iterator<int>(std::cout, ","));
    
      std::cout << std::endl;  
    }
    

Anmelden zum Antworten