Comment implémenter un constructeur de copie pour une classe qui a une unique_ptr
variable membre? Je ne considère que C ++ 11.
c++
c++11
unique-ptr
codefx
la source
la source
std::vector
.unique_ptr
, vous voulez probablement un constructeur de déplacement, si votre objectif est de placer les données dans un fichierstd::vector
. D'un autre côté, le standard C ++ 11 a créé automatiquement des constructeurs de déplacement, alors peut-être que vous voulez un constructeur de copie ...Réponses:
Étant donné que le
unique_ptr
ne peut pas être partagé, vous devez soit profondément copier son contenu ou convertirunique_ptr
à unshared_ptr
.Vous pouvez, comme NPE l'a mentionné, utiliser un move-ctor au lieu d'un copy-ctor, mais cela entraînerait une sémantique différente de votre classe. Un move-ctor devrait rendre le membre déplaçable explicitement via
std::move
:Disposer d'un ensemble complet d'opérateurs nécessaires conduit également à
Si vous voulez utiliser votre classe dans a
std::vector
, vous devez essentiellement décider si le vecteur sera le propriétaire unique d'un objet, auquel cas il suffirait de rendre la classe déplaçable, mais pas copiable. Si vous omettez le copy-ctor et le copy-assignment, le compilateur vous guidera sur la façon d'utiliser un std :: vector avec des types move-only.la source
int
. Si vous avez ununique_ptr<Base>
qui stocke unDerived
, ce qui précède sera coupé.A( const A& a ) : up_( a.up_ ? new int( *a.up_ ) : nullptr) {}
value_ptr
-unique_ptr
plus d'informations de suppression / copieur.Le cas habituel pour avoir un
unique_ptr
dans une classe est de pouvoir utiliser l'héritage (sinon un objet simple ferait souvent l'affaire, voir RAII). Pour ce cas, il n'y a pas de réponse appropriée dans ce fil jusqu'à présent .Alors, voici le point de départ:
... et le but est, comme dit, de rendre
Foo
copiable.Pour cela, il faut faire une copie complète du pointeur contenu pour s'assurer que la classe dérivée est copiée correctement.
Cela peut être accompli en ajoutant le code suivant:
Il se passe essentiellement deux choses ici:
Le premier est l'ajout de constructeurs de copie et de déplacement, qui sont implicitement supprimés dans
Foo
lorsque le constructeur de copie deunique_ptr
est supprimé. Le constructeur de déplacement peut être ajouté simplement par= default
... ce qui est juste pour indiquer au compilateur que le constructeur de déplacement habituel ne doit pas être supprimé (cela fonctionne, comme aunique_ptr
déjà un constructeur de déplacement qui peut être utilisé dans ce cas).Pour le constructeur de copie de
Foo
, il n'existe pas de mécanisme similaire car il n'y a pas de constructeur de copie deunique_ptr
. Il faut donc en construire un nouveauunique_ptr
, le remplir avec une copie de la pointee d'origine et l'utiliser comme membre de la classe copiée.En cas d'héritage, la copie de la pointee originale doit être faite avec soin. La raison en est que faire une simple copie via
std::unique_ptr<Base>(*ptr)
dans le code ci-dessus entraînerait un découpage, c'est-à-dire que seul le composant de base de l'objet est copié, tandis que la partie dérivée est manquante.Pour éviter cela, la copie doit être effectuée via le clone-pattern. L'idée est de faire la copie via une fonction virtuelle
clone_impl()
qui retourne aBase*
dans la classe de base. Dans la classe dérivée, cependant, il est étendu via la covariance pour renvoyer aDerived*
, et ce pointeur pointe vers une copie nouvellement créée de la classe dérivée. La classe de base peut alors accéder à ce nouvel objet via le pointeur de classe de baseBase*
, l'envelopper dans ununique_ptr
, et le renvoyer via laclone()
fonction réelle qui est appelée de l'extérieur.la source
unique_ptr
alors que le confinement direct ferait autrement. La réponse??? L'héritage .unique_ptr
plusoptional
pour les types nullable.clone_impl
base in, le compilateur ne vous dira pas si vous l'oubliez dans la classe dérivée. Vous pouvez cependant utiliser une autre classe de baseCloneable
et y implémenter un pur virtuelclone_impl
. Ensuite, le compilateur se plaindra si vous l'oubliez dans la classe dérivée.Essayez cet assistant pour créer des copies complètes et faites face lorsque la source unique_ptr est nulle.
Par exemple:
la source
Daniel Frey mentionne la solution de copie, je parlerais de la façon de déplacer l'unique_ptr
Ils sont appelés constructeur de déplacement et affectation de déplacement
tu pourrais les utiliser comme ça
Vous devez envelopper a et c par std :: move car ils ont un nom std :: move indique au compilateur de transformer la valeur en rvalue reference quels que soient les paramètres Au sens technique, std :: move est une analogie avec quelque chose comme " std :: rvalue "
Après le déplacement, la ressource de l'unique_ptr est transférée vers un autre unique_ptr
Il existe de nombreux sujets qui documentent la référence rvalue; c'est assez facile pour commencer .
Éditer :
L'objet déplacé doit rester dans un état valide mais non spécifié .
C ++ primer 5, ch13 donne également une très bonne explication sur la façon de "déplacer" l'objet
la source
a
après avoir appelé std :: move (a) dans leb
constructeur de mouvement? Est-ce simplement totalement invalide?Je suggère d'utiliser make_unique
la source
unique_ptr
n'est pas copiable, il est uniquement déplaçable.Cela affectera directement Test, qui est, dans votre deuxième exemple, également uniquement mobile et non copiable.
En fait, il est bon que vous utilisiez
unique_ptr
ce qui vous protège d'une grosse erreur.Par exemple, le principal problème avec votre premier code est que le pointeur n'est jamais supprimé, ce qui est vraiment très mauvais. Dites, vous régleriez cela en:
C'est également mauvais. Que se passe-t-il si vous copiez
Test
? Il y aura deux classes qui ont un pointeur qui pointe vers la même adresse.Quand on
Test
est détruit, cela détruira également le pointeur. Lorsque votre deuxièmeTest
est détruit, il essaiera également de supprimer la mémoire derrière le pointeur. Mais il a déjà été supprimé et nous obtiendrons une mauvaise erreur d'exécution d'accès à la mémoire (ou un comportement indéfini si nous ne sommes pas chanceux).Donc, la bonne façon est d'implémenter le constructeur de copie et l'opérateur d'affectation de copie, de sorte que le comportement soit clair et que nous puissions créer une copie.
unique_ptr
est bien en avance sur nous ici. Il a la signification sémantique: " Je suisunique
, donc vous ne pouvez pas simplement me copier. " Donc, cela nous évite de l'erreur d'implémenter maintenant les opérateurs à portée de main.Vous pouvez définir le constructeur de copie et l'opérateur d'affectation de copie pour un comportement spécial et votre code fonctionnera. Mais vous êtes, à juste titre (!), Obligé de le faire.
La morale de l'histoire: toujours utiliser
unique_ptr
dans ce genre de situations.la source