Je pense à un cas d'utilisation étrange où je veux déplacer un objet vers un nullptr. Peut-être devrais-je donner un fragment de code:
class Objpair { public: Objpair(Obj&& a, Obj&&b) : first(&a), second(&b) { } private: Obj* first; Obj* second; };
Le problème est que lorsque a et b sont hors de portée, le premier et le deuxième pointeur seront suspendus. Si je peux déplacer Object a sur le premier pointeur, il ne devrait y avoir aucun problème de problèmes de double libre et de portée. Si le membre était d'abord déclaré comme pointeur Obj et non Obj *, alors le premier droit (std :: move (a)) ferait le travail. Je dois faire quelque chose de mal ici. Je pense déplacer au lieu de copier car j'essaye de transférer le contrôle d'un autre objet vers l'objet courant et d'améliorer les performances.
La version pointeur est utilisée car je pense au comportement polymorphe du membre objet.
4 Réponses :
Vous ne pouvez pas déplacer des objets sur des pointeurs (nullptr ou autre). Vous ne pouvez pas non plus prolonger la durée de vie des objets en faisant pointer des pointeurs vers eux - C ++ ne fonctionne tout simplement pas de cette façon.
Puisque vous avez besoin d'un comportement polymorphe, vous ne pouvez pas simplement copier-construire ou affecter un Obj code> pointeur. Vous voudrez probablement quelque chose comme une méthode
clone ()
pour la classe Obj
, comme indiqué dans cette question:
pour en savoir plus. Souvenez-vous cependant que vous ne pouvez pas prolonger la durée de vie de la référence rvalue, vous devez donc construire un nouvel objet d'une manière ou d'une autre. N'oubliez pas également d'éviter les fuites de mémoire. Donc, si votre clone ()
produit un Obj *
, vous devrez écrire quelque chose comme:
template <typename T1, typename T2> class Objpair { public: Objpair(T1&& a, T2&&b) : first(a), second(b) { } private: T1 first; T2 second; };
sur peut-être std :: shared_ptr
au lieu de les unique_ptr
(voir cette réponse sur le choix entre les deux).
Si vous avez besoin d'un comportement "polymorphe", mais que vous connaissez le types exacts de a et b au moment de la compilation, et cela ne vous dérange pas d'avoir différentes classes ObjPair
, vous pouvez également essayer quelque chose comme ceci:
class Objpair { public: Objpair(Obj&& a, Obj&&b) : first(a.clone()), second(b.clone()) { } private: std::unique_ptr<Obj> first; std::unique_ptr<Obj> second; };
auquel cas aucun pointeur n'est impliqué (!)
Dans un commentaire, vous avez dit
Le premier et le second peuvent pointer vers BaseClass, Derived1, Derived2,
Dans ce cas, votre meilleure option est de fournir une fonction membre virtual
dans Obj
pour cloner un objet.
class Objpair { public: Objpair(Obj&& a, Obj&&b) : first(a.clone()), second(b.clone()) { } private: Obj* first; Obj* second; };
et utilisez-le pour initialiser d'abord
et second
.
class Obj { public: virtual Obj* clone() const = 0; ... }
et assurez-vous que vous prendre soin de désallouer la mémoire pointée par premier
et deuxième
dans le destructeur. Voir La règle de trois pour plus de détails.
Vous pouvez utiliser des pointeurs intelligents pour éliminer le besoin de code dans votre classe pour gérer la durée de vie des objets. Que vous utilisiez std :: unique_ptr
ou std :: shared_ptr
dépend de la façon dont vous souhaitez utiliser Objpair
.
Ce que vous pouvez faire est de déplacer les objets de construction de vos paramètres comme ceci:
class Objpair { public: Objpair(Obj&& a, Obj&& b) : first(std::make_unique<Obj>(std::move(a))) , second(std::make_unique<Obj>(std::move(b))) {} private: std::unique_ptr<Obj> first; std::unique_ptr<Obj> second; };
Je vous recommande d'utiliser std :: unique_ptr
:
class Objpair { public: Objpair(Obj&& a, Obj&& b) : first(new Obj(std::move(a))) , second(new Obj(std::move(b))) {} private: Obj* first; Obj* second; };
La construction sera une construction move sans avoir besoin de std :: move ()
, puisque le ctor obtient des références rvalue pour commencer.
@einpoklum Non, vous devez std :: move
car une fois que les références-valeurs-r sont liées, elles deviennent des références-valeurs-l . Vous devez soit std :: move
soit std :: forward
(pour transférer les références) à chaque étape.
unique ou shared_ptr sont de bons choix. Le pointeur nu est toujours plus difficile à suivre.
Les références @Galik rvalue ne «deviennent pas des références lvalue» ici. Mais vous avez raison de dire que std :: move
est nécessaire. Elle est nécessaire car l'expression id a
est une expression lvalue - quel que soit le type de l'expression qui est la référence rvalue.
La réponse acceptée par @ Galik est bonne. Bien que cela nécessite que Obj
soit au moins déplaçable, ce qui est similaire à d'autres réponses avec les solutions Clone ()
- elles mettent toutes certaines exigences sur Obj
et ses descendants. Pour supprimer cette restriction, nous pouvons changer le constructeur de Objpair
pour accepter std::unique_ptr
:
#include <memory> class Obj { public: Obj() {} // non-copyable/non-movable Obj(const Obj&) = delete; Obj& operator=(const Obj&) = delete; }; class Objpair { public: Objpair(std::unique_ptr<Obj>&& a, std::unique_ptr<Obj>&& b) : first(std::move(a)) , second(std::move(b)) {} private: std::unique_ptr<Obj> first; std::unique_ptr<Obj> second; }; int main() { Objpair p{std::make_unique<Obj>(), std::make_unique<Obj>()}; return 0; }
Les bonus possibles sont:
a
et b
avant de les transmettre à Objpair
, de manière polymorphe.
Si le membre était d'abord déclaré comme pointeur Obj et non Obj * , alors le premier droit (std :: move (a)) ferait l'affaire. Pourquoi n'êtes-vous pas D'après cela?
Quel est le sens de stocker les adresses des expressions rvalue?
a
etb
sont des objets qui sont probablement sur le point de mourir. Qu'essayez-vous d'accomplir?le premier et le second peuvent pointer vers la classe de base, dérivé 1, dérivé2, ...
"se déplacer vers un pointeur non initialisé" n'a aucun sens pour moi. Vous devez avoir un
Obj
réel pour y accéder. "quand a et b sont hors de portée, le premier et le deuxième pointeur seront en suspension" Pas quanda
etb
sont détruits. Lorsque les objets que vous avez utilisés pour les initialiser sont détruits.Le fragment est erroné, ne le précisant pas d'une manière ou d'une autre. Comme mentionné, ne devrait pas utiliser cela. Vous recherchez un meilleur design.
Dans un environnement multi-thread, une partie du code crée des objets; une autre partie du code les récupère et les compose dans un type différent. Je pourrais faire avec la version pointeur. Je cherche juste la possibilité d'utiliser la référence d'objet et de convertir des pointeurs. Une telle syntaxe est-elle même possible en C ++.
@KeminZhou, il est assez difficile de dire ce que vous essayez d'accomplir. Je crains qu'il ne s'agisse d'un problème X / Y ou que vous essayez de faire une optimisation prématurée. Pourriez-vous expliquer le problème global que vous essayez de résoudre et pourquoi cela vous a amené à essayer cette conception?