web-dev-qa-db-fra.com

Pourquoi un faiblesse_ptr ne peut-il pas être construit à partir d'un unique_ptr?

Si je comprends bien, un weak_ptr n'incrémente pas le nombre de références de l'objet géré, il ne représente donc pas la propriété. Il vous permet simplement d'accéder à un objet dont la durée de vie est gérée par quelqu'un d'autre. Je ne vois donc pas vraiment pourquoi un weak_ptr ne peut pas être construit à partir d'un unique_ptr, mais seulement un shared_ptr.

Quelqu'un peut-il expliquer brièvement cela?

48
adam10603

std::weak_ptr Ne peut être utilisé que si vous le convertissez en std::shared_ptr Au moyen de lock(). si la norme autorise ce que vous suggérez, cela signifie que vous devez convertir std :: faible_ptr en unique afin de l'utiliser, violant l'unicité (ou réinventant std::shared_ptr)

Afin d'illustrer, regardez les deux morceaux de code:

std::shared_ptr<int> shared = std::make_shared<int>(10);
std::weak_ptr<int> weak(shared);

{
*(weak.lock()) = 20; //OK, the temporary shared_ptr will be destroyed but the pointee-integer still has shared  to keep it alive
}

Maintenant avec votre suggestion:

std::unique_ptr<int> unique = std::make_unique<int>(10);
std::weak_ptr<int> weak(unique);

{
*(weak.lock()) = 20; //not OK. the temporary unique_ptr will be destroyed but unique still points at it! 
}

Cela dit, vous pouvez suggérer qu'il n'y a qu'un seul unique_ptr, Et vous pouvez toujours déréférencer weak_ptr (Sans en créer un autre unique_ptr) Alors il n'y a pas de problème. Mais alors quelle est la différence entre unique_ptr Et shared_ptr Avec une seule référence? ou plus, quelle est la différence entre un unique_ptr normal et des pointeurs C et un get en utilisant get?

weak_ptr N'est pas pour les "ressources générales non propriétaires", il a un travail très spécifique - Le but principal de weak_ptr Est d'empêcher le pointage circulaire de shared_ptr Qui fera une fuite de mémoire . Tout le reste doit être fait avec les simples unique_ptr Et shared_ptr.

16
David Haim

Si vous y réfléchissez, un weak_ptr doit faire référence à autre chose qu'à l'objet lui-même. En effet, l'objet peut cesser d'exister (lorsqu'il n'y a plus de pointeurs puissants) et le weak_ptr doit encore faire référence à quelque chose qui contient l'information que l'objet n'existe plus.

Avec un shared_ptr, que quelque chose est la chose qui contient le nombre de références. Mais avec un unique_ptr, il n'y a pas de compte de référence, il n'y a donc rien qui contienne le compte de référence, donc rien ne continue d'exister lorsque l'objet est parti. Il n'y a donc rien pour un weak_ptr se référer à.

Il n'y aurait pas non plus de moyen sensé d'utiliser un tel weak_ptr. Pour l'utiliser, vous devez avoir un moyen de garantir que l'objet n'a pas été détruit pendant que vous l'utilisiez. C'est facile avec un shared_ptr - c'est ce que shared_ptr Est-ce que. Mais comment faites-vous cela avec un unique_ptr? Vous ne pouvez évidemment pas en avoir deux, et quelque chose d'autre doit déjà être propriétaire de l'objet, sinon il aurait été détruit car votre pointeur est faible.

32
David Schwartz

UNE shared_ptr se compose essentiellement de deux parties:

  1. l'objet pointé
  2. l'objet de comptage de référence

Une fois que le nombre de références tombe à zéro, l'objet (# 1) est supprimé.

Maintenant, un weak_ptr doit pouvoir savoir si un objet existe toujours. Pour ce faire, il doit pouvoir voir l'objet de comptage de référence (# 2) s'il n'est pas nul, il peut créer un shared_ptr pour l'objet (en incrémentant le nombre de références). Si le nombre est nul, il renverra un shared_ptr.

Considérez maintenant quand l'objet de comptage de référence (# 2) peut être supprimé? Nous devons attendre qu'aucun shared_ptr OR weak_ptr objet s'y réfère. À cet effet, l'objet comptage de référence contient deux comptages de référence, un fort ref et un faible réf. L'objet de comptage de référence ne sera supprimé que lorsque ses deux comptages sont nuls. Cela signifie qu'une partie de la mémoire ne peut être libérée qu'après la disparition de toutes les références faibles (cela implique un inconvénient caché avec make_shared ).

tl; dr; weak_ptr dépend d'un nombre de références faible qui fait partie de shared_ptr, il ne peut pas y avoir weak_ptr sans un shared_ptr.

12
Motti

Sur le plan conceptuel, rien n'empêche une implémentation où un faible_ptr ne fournit qu'un accès et un unique_ptr contrôle la durée de vie. Cependant, cela pose des problèmes:

  • unique_ptr n'utilise pas le comptage de références pour commencer. L'ajout de la structure de gestion pour gérer les références faibles serait possible, mais nécessiterait une allocation dynamique supplémentaire. Puisque unique_ptr est censé éviter toute surcharge d'exécution (!) sur un pointeur brut, cette surcharge n'est pas acceptable.
  • Pour utiliser l'objet référencé par un weak_ptr, vous devez en extraire une référence "réelle", ce qui validera d'abord que le pointeur n'est pas expiré en premier et vous donnera ensuite cette référence réelle (un shared_ptr dans ce cas). Cela signifie que vous avez soudainement une deuxième référence à un objet qui est censé appartenir uniquement, ce qui est une recette pour les erreurs. Cela ne peut pas être résolu en renvoyant un pointeur mixte demi-fort qui ne retarde que temporairement la destruction éventuelle de la pointe, car vous pourriez tout aussi bien stocker celui-ci, en défaisant l'idée derrière unique_ptr.

Je me demande simplement quel problème essayez-vous de résoudre en utilisant un weak_ptr ici?

8
Ulrich Eckhardt

Personne n'a encore mentionné l'aspect performance du problème, alors laissez-moi jeter mes 0,02 $.

weak_ptr Doit en quelque sorte savoir quand les shared_ptr Correspondants sont tous hors de portée et que l'objet pointé a été désalloué et détruit. Cela signifie que shared_ptr Doit en quelque sorte communiquer la destruction vers chaque weak_ptr Au même objet. Cela a un certain coût - par exemple, une table de hachage globale doit être mise à jour, où weak_ptr Obtient l'adresse (ou nullptr si l'objet est détruit).

Cela implique également le verrouillage dans un environnement multithread, il peut donc être trop lent pour certaines tâches.

Cependant, l'objectif de unique_ptr Est de fournir une classe d'abstraction de type zéro coût RAII. Par conséquent, il ne devrait pas entraîner d'autre coût que celui de deleteing (ou delete[] Ing) l'objet alloué dynamiquement. Le retard imposé en effectuant un accès à une table de hachage verrouillée ou autrement protégée, par exemple, peut être comparable au coût de la désallocation, ce qui n'est pas souhaitable dans le cas de unique_ptr.

On dirait que tout le monde écrit ici sur std :: faiblesse_ptr mais pas sur le concept de poineur faible qui, je crois, est ce que l'auteur demande

Je pense que personne n'a mentionné pourquoi la bibliothèque standard ne fournit pas de faiblesse_ptr pour unique_ptr. Le pointeur faible CONCEPT ne rejette pas l'utilisation unique_ptr. Un pointeur faible n'est qu'une information si l'objet a déjà été supprimé, il ne s'agit donc pas d'un motif d'observation généralisé magique mais très simple.

C'est à cause de la sécurité des threads et de la cohérence avec shared_ptr.

Vous ne pouvez tout simplement pas garantir que votre faiblesse_ptr (créée à partir de unique_ptr existant sur un autre thread) ne sera pas détruite lors de l'appel de la méthode sur un objet pointé. C'est parce que faible_ptr doit être cohérent avec std :: shared_ptr qui garantit la sécurité des threads. Vous pouvez implémenter faiblesse_ptr qui fonctionne correctement avec unique_ptr mais uniquement sur la même méthode de verrouillage de thread sera inutile dans ce cas. Vous pouvez vérifier les sources de chrome pour base :: WeakPtr et base :: WeakPtrFactory - vous pouvez l'utiliser librement avec unique_ptr. Le code du pointeur faible en chrome est probablement basé sur la destruction du dernier membre - vous devez ajouter l'usine en tant que dernier membre et après cela, je crois que WeakPtr est informé de la suppression d'objet (je ne suis pas sûr à 100%) - donc il ne semble pas si difficile à mettre en œuvre.

Globalement, l'utilisation de unique_ptr avec le concept de pointeur faible est OK IMHO.

3
Bartosz Piękny

Il peut être utile de distinguer les raisons de préférer un unique_ptr au cours d'une shared_ptr.

Performance Une raison évidente est le temps de calcul et l'utilisation de la mémoire. Tel que défini actuellement, shared_ptr les objets ont généralement besoin de quelque chose comme une valeur de comptage de référence, qui prend de l'espace et doit également être activement maintenue. unique_ptr les objets non.

Sémantique Avec un unique_ptr, en tant que programmeur, vous avez une assez bonne idée quand l'objet pointé va être détruit: lorsque le unique_ptr est détruit ou lorsque l'une de ses méthodes de modification est appelée. Et ainsi de suite sur des bases de code volumineuses ou inconnues, en utilisant unique_ptr transmet statiquement (et applique) certaines informations sur le comportement d'exécution du programme qui peuvent ne pas être évidentes.

Les commentaires ci-dessus se sont généralement concentrés sur les raisons fondées sur les performances qu'il ne serait pas souhaitable pour weak_ptr objets à lier à unique_ptr objets. Mais on peut se demander si l'argument basé sur la sémantique est une raison suffisante pour qu'une future version de la STL prenne en charge le cas d'utilisation impliqué par la question d'origine.

0
Christian Convey