it-swarm.com.de

vektor von unique_ptr in C++ 11

Ich bin vor kurzem zu C++ 11 gewechselt und versuche mich dort an gute Praktiken zu gewöhnen. Was ich am Ende sehr oft erledige, ist etwa Folgendes:

class Owner
{
private:
    vector<unique_ptr<HeavyResource>> _vectorOfHeavyResources;
public:
    virtual const vector<const HeavyResource*>* GetVectorOfResources() const;
};

Dazu muss ich beispielsweise einen _returnableVector hinzufügen und die Quellvektoren übersetzen, um sie später wiedergeben zu können:

_returnableVector = vector<HeavyResource*>;
for (int i=0; i< _vectorOfHeavyResources.size(); i++)
{
    _returnableVector.Push_back(_vectorOfHeavyResources[i].get());
}

Hat jemand ein ähnliches Problem bemerkt? Was sind deine Gedanken und Lösungen? Bekomme ich die gesamte Eigentümeridee genau hier?

UPDATE: Hier ist noch etwas: Was ist, wenn eine Klasse ein Ergebnis einer Verarbeitung als vector<unique_ptr<HeavyResource>> zurückgibt (es überträgt den Besitz der Ergebnisse an den Aufrufer) und es für einige verwendet werden soll Weiterverarbeitung:

vector<unique_ptr<HeavyResource>> partialResult = _processor1.Process();
// translation
auto result = _processor2.Process(translatedPartialResult); // the argument of process is vector<const HeavyResource*>
12
Witek

Ich würde vorschlagen, dass Sie, anstatt einen un-unique_ptred-Vektor zu verwalten und zurückzugeben, Funktionen bereitstellen, um direkt auf die Elemente zuzugreifen. Dies kapselt die Speicherung Ihrer Ressourcen. Kunden wissen nicht, dass sie als unique_ptrs gespeichert sind oder dass sie in einer vector gespeichert werden.

Eine Möglichkeit hierfür ist, boost::indirect_iterator zu verwenden, um den unique_ptr automatisch dereferenzieren zu können:

using ResourceIterator =
     boost::indirect_iterator<std::vector<std::unique_ptr<HeavyResource>>::iterator,
                              const HeavyResource>;
ResourceIterator begin() { return std::begin(_vectorOfHeavyResources); }
ResourceIterator end() { return std::end(_vectorOfHeavyResources); }

Demo

17
TartanLlama

Wenn Sie dies häufig feststellen, kann es sinnvoll sein, eine Klasse zu schreiben, die sich wie ein unique_ptr verhält, aber die Konstante des Zeigers an das Objekt weitergibt, auf das sie verweist. Auf diese Weise können Sie einfach eine const-Referenz an Ihren Vektor zurückgeben. 

Ich habe das einmal geschrieben und bin damit fertig:

#include <iostream>
#include <vector>
#include <memory>

//unique,const-preserving pointer
template<class T>
class ucp_ptr {
    std::unique_ptr<T> ptr;
public:
    ucp_ptr() = default;
    ucp_ptr(T* ptr) :ptr{ ptr }{};
    ucp_ptr(std::unique_ptr<T>&& other) :ptr(std::move(other)){};

    T&        operator*()       { return ptr.get(); }
    T const & operator*()const  { return ptr.get(); }

    T*        operator->()      { return ptr.get(); }
    T const * operator->()const { return ptr.get(); }
};

struct Foo {
    int a = 0;
};

int main() {
    std::vector<ucp_ptr<Foo>> v;
    v.emplace_back(new Foo());
    v.emplace_back(std::make_unique<Foo>());    

    v[0]->a = 1;
    v[1]->a = 2;

    const std::vector<ucp_ptr<Foo>>& cv = v;

    std::cout << cv[0]->a << std::endl; //<-read access OK
    //cv[1]->a = 10; //<-compiler error
}

Natürlich können Sie es ein wenig erweitern, wenn Sie benutzerdefinierte Löscher benötigen oder eine Spezialisierung zum Verwalten von Arrays hinzufügen möchten. Dies ist jedoch die Basisversion. Ich glaube auch, dass ich hier auf SO eine verfeinerte Version davon gesehen habe, aber ich kann sie jetzt nicht finden.

Hier ist ein Beispiel, wie dies in einer Klasse verwendet werden kann:

class Bar {
    std::vector<ucp_ptr<Foo>> v;
public:
    void add(const Foo& foo){ 
        v.Push_back(std::make_unique<Foo>(foo)); 
    }
    //modifying elements
    void doubleElements() {
        for (auto& e : v){
            e->a *= 2;
        }
    }
    const std::vector<ucp_ptr<Foo>>& showElements() const{
        return v;
    }
};

BEARBEITEN

Was Ihr Update betrifft, müssen Sie mit der Tatsache leben, dass vector<T> nicht mit vector<B> zusammenhängt, selbst wenn es gültig wäre, T in B umzuwandeln und umgekehrt.
Sie können Adapter schreiben, die Ihnen eine andere Sicht auf die Elemente geben (indem Sie bei Bedarf jedes Element umwandeln), aber abgesehen von der Erstellung eines neuen Vektors des richtigen Typs gibt es keinen allgemeinen Meachanismus (das ist mir bewusst) of) zu tun, was Sie wollen. 

1
MikeMB

Sie sollten stattdessen shared_ptr in Betracht ziehen, da dies wahrscheinlich das widerspiegelt, was Sie erreichen wollen. Sie möchten unique_ptr wahrscheinlich in kleineren Bereichen verwenden oder immer, wenn Sie nur eine Sache mit einem Objekt wiedergeben möchten.

0
Tas