2014-05-18 11 views
10

std::unique_ptr eine gelöschte Copykonstruktor hat, was bedeutet, dass, wenn Sie einen unique_ptr in Ihrer Klasse Foo als Datenelement haben, dann müssen Sie Ihre eigene Kopie Konstruktor für Foo und manuell tief Kopie schreiben, dass Mitglied (auch wenn Der compilergenerierte Kopierkonstruktor wäre für alle anderen Mitglieder in Ordnung.Auto-Klonen unique_ptr

Um in einer polymorphen Weise kopieren zu können, kann das Methodenmuster clone() verwendet werden. Nehmen wir an, unsere Objekte ein Klon Verfahren wie dieses:

class Base { 
    virtual std::unique_ptr<Base> clone() = 0; 
}; 

Foo nun wie folgt aussieht:

class Foo { 
public: 
    ... 
    Foo(Foo const& other) 
     : b(other.b->clone()) 
     , // init 10 more members that could otherwise be auto-copied just fine 
      // with the automatically generated copy constructor 
    {} 
    ... 

private: 
    std::unique_ptr<Base> b; 
    //10 more data members 

}; 

Nun fand ich einen Weg, um Auto-Klon Foo::b, durch einen Wrapper über unique_ptr schreiben, die definiert den Kopierkonstruktor und die Zuweisung durch Aufruf von clone.

template <typename T> 
class auto_cloned_unique_ptr 
{ 
private: 
    std::unique_ptr<T> up; 

public: 
    // copy constructor 
    auto_cloned_unique_ptr(auto_cloned_unique_ptr<T> const& other) 
     : up(other.up->clone()) {} 

    // copy assignment 
    auto_cloned_unique_ptr<T>& operator =(auto_cloned_unique_ptr<T> const& other) 
    { 
     this->up = other.up->clone(); 
     return *this; 
    } 

    auto_cloned_unique_ptr(std::unique_ptr<T> _up) 
     : up(std::move(_up)) {} 

    // Delegate everything else to unique_ptr 
    auto_cloned_unique_ptr(auto_cloned_unique_ptr<T>&& other) 
     : up(std::move(other.up)) {} 

    auto_cloned_unique_ptr<T>& operator =(auto_cloned_unique_ptr<T>&& other) 
    { 
     this->up = std::move(other.up); 
     return *this; 
    } 

    auto operator *() const {return *up;} 
    auto operator->() const {return up.operator->();} 
    auto get() -> const {return up.get();} 

}; 

Nun, wenn wir verwenden diese brauchen wir nicht unsere eigenen Kopierkonstruktor zu definieren:

class Foo2 { 
public: 
    ... 

private: 
    auto_cloned_unique_ptr<Base> b; 
    //10 more data members 

}; 

Ist ein solcher Ansatz sehr verpönt (für einen Nicht-Standard-Wrapper über unique_ptr verwenden) ?

+3

Es gab viele solche Vorschläge. Suchen Sie nach 'value_ptr', um nur einen zu nennen. Im Allgemeinen ist es schwierig zu garantieren, dass dies richtig angewendet wird, und daher fühlt es sich nicht richtig an, in den Standard zu gehören. –

+0

Übrigens, warum nicht "private" von "unique_ptr" erben und Konstruktorvererbung verwenden? – Columbo

+1

* "Ist solch ein Ansatz sehr verpönt" * Ist eine Frage, die besser auf [CodeReview] (http://codereview.stackexchange.com) IMO passt. – dyp

Antwort

0

Dieser Ansatz ist in Ordnung, aber Sie sollten sehr vorsichtig sein, Ihre Objekte nicht zu klonen, wenn Sie es nicht wollten.

inherriting Auch von unique_ptr Leistung verbessern könnte

+0

Das Vererben von 'unique_ptr' würde in diesem Fall nie helfen, da es keine leere Basisoptimierung dafür gibt (es ist niemals eine leere Klasse) – Justin

1

Das Problem mit dem Ansatz ist, dass es die Bedeutung eines unique_ptr ändert. Der Schlüssel zu einem unique_ptr ist, dass er angibt, wer der Besitzer eines Objekts ist. Wenn Sie einen Kopierkonstruktor für unique_ptr hinzufügen, was bedeutet das? Kopieren Sie das Eigentum? A und B beide einzigartig eigene das Ding? Das macht keinen Sinn. Wenn sie das Eigentumsrecht teilen, sollten Sie shared_ptr verwenden, um den gemeinsamen Besitz anzugeben. Wenn Sie einen eindeutigen Eigentümer einer Kopie des Objekts haben möchten, geben Sie dies natürlich mit make_unique (* pFoo) an. Bei Basis- und abgeleiteten Objekten hat das Basisobjekt
virtual unique_ptr < Foo> Clone() const = 0;
ist ein vollkommen normales Konstrukt. Das heißt, die abgeleiteten Klassen wissen, wie sie sich selbst kopieren, damit sie keine geschnittene Kopie erzeugen, aber sie geben ein unique_ptr an die Basisklasse zurück, um anzuzeigen, dass Sie die von ihnen erzeugte Kopie besitzen. Innerhalb dieser Klonoperationen müssen Sie explizit nicht kopierbare Elemente der abgeleiteten Klassen behandeln, so dass Sie nicht einfach einen Standardkopie-Konstruktor oder einen generierten Kopierkonstruktor verwenden können. Sie müssen antworten: "Was bedeutet es, etwas zu kopieren, das dieses Ding enthält, das nicht kopiert werden kann?"
Als ein konkretes Beispiel, was würde es bedeuten, eine abgeleitete Klasse mit einem Mutex zu kopieren? Was wenn es verschlossen wäre und ein anderer Thread darauf wartete? Sehen Sie, warum es schwierig ist, eine allgemeine Antwort zu geben?