Pourquoi est-ce qu’un faiblesse_ptr ne peut pas être construit à partir d’un unique_ptr?

Si je comprends bien, un weak_ptr pas le compte de référence de l’object géré, par conséquent, il ne représente pas la propriété. Il vous permet simplement d’accéder à un object dont la durée de vie est gérée par quelqu’un d’autre. Donc, je ne vois 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?

std::weak_ptr ne peut être utilisé que si vous le convertissez en std::shared_ptr par le biais de lock() . si la norme autorisait ce que vous proposez, cela signifie que vous devez convertir std :: faiblesse_ptr en unique afin de l’utiliser, en violation de l’unicité (ou de réinvention de std::shared_ptr )

Pour illustrer, regardez les deux morceaux de code:

 std::shared_ptr shared = std::make_shared(10); std::weak_ptr weak(shared); { *(w.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 unique = std::make_unique(10); std::weak_ptr weak(unique); { *(weak.lock()) = 20; //not OK. the temporary unique_ptr will be destroyed but unique still points at it! } 

Cela a été dit, vous pouvez suggérer qu’il n’ya qu’un unique_ptr , et que vous pouvez toujours déréférer weak_ptr (sans créer un autre unique_ptr ), alors il n’ya 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 régulier et des C-pointers et un get en utilisant get ?

weak_ptr n’est pas destiné aux “ressources non propriétaires générales”, 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 provoquera une fuite de mémoire. Tout le rest doit être fait avec plain unique_ptr et shared_ptr .

Si vous y réfléchissez, un weak_ptr doit faire référence à autre chose que l’object lui-même. C’est parce que l’object peut cesser d’exister (quand il n’y a plus de pointeurs forts) et que la weak_ptr doit encore faire référence à quelque chose qui contient les informations indiquant que l’object n’existe plus.

Avec shared_ptr , quelque chose est la chose qui contient le compte de référence. Mais avec un unique_ptr , il n’y a pas de compte de référence, donc il n’y a rien qui contienne le compte de référence, donc rien à continuer à exister lorsque l’object est parti. Il n’y a donc rien à faire référence à un weak_ptr .

Il n’y aurait pas non plus de moyen raisonnable d’utiliser une telle weak_ptr . Pour l’utiliser, vous devez avoir un moyen de garantir que l’object n’a pas été détruit pendant que vous l’utilisiez. C’est simple avec un shared_ptr – c’est ce que fait un shared_ptr . 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à posséder l’object ou il aurait été détruit puisque votre pointeur est faible.

Un shared_ptr comprend essentiellement deux parties:

  1. l’object pointé
  2. l’object de compte de référence

Une fois que le compte de référence tombe à zéro, l’object (# 1) est supprimé.

Maintenant, un weak_ptr doit être capable de savoir si un object existe toujours. Pour ce faire, il doit être capable de voir l’object de comptage de référence (# 2) si ce n’est pas zéro, il peut créer un shared_ptr pour l’object (en incrémentant le compte de référence). Si le compte est nul, il retournera un shared_ptr vide.

Maintenant, considérez quand l’object de compte de référence (# 2) peut être supprimé? Nous devons attendre qu’aucun object shared_ptr OR weak_ptr ne s’y réfère. Pour cela, l’object de comptage de référence contient deux comptages de référence, un ref fort et un ref faible . L’object 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 que toutes les références faibles ont disparu (ceci implique un inconvénient caché avec make_shared ).

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

D’un sharepoint vue conceptuel, rien n’empêche une implémentation où faiblesse_ptr fournit uniquement un access et un unique_ptr contrôle la durée de vie. Cependant, il y a des problèmes avec cela:

  • 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écessite une allocation dynamic supplémentaire. unique_ptr étant censé éviter toute surcharge (!) À l’exécution sur un pointeur brut, cette surcharge n’est pas acceptable.
  • Pour utiliser l’object référencé par un weak_ptr , vous devez en extraire une référence “réelle”, 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 object supposé être détenu de manière unique, ce qui est une recette pour les erreurs. Cela ne peut pas être corrigé en renvoyant un pointeur mixte à moitié fort qui ne retarde que temporairement la destruction possible de la pointe, car vous pourriez aussi bien stocker celle-ci, en vainquant l’idée derrière unique_ptr .

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

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

weak_ptr doit en quelque sorte savoir quand les weak_ptr shared_ptr correspondants sont tous hors de scope et que l’object pointé a été désalloué et détruit. Cela signifie que shared_ptr doit communiquer la destruction vers chaque weak_ptr sur le même object. Cela a un certain coût – par exemple, une table de hachage globale doit être mise à jour, où weak_ptr obtient l’adresse de (ou nullptr si l’object est détruit).

Cela implique également le locking dans un environnement multithread, de sorte qu’il peut potentiellement être trop lent pour certaines tâches.

Cependant, le but de unique_ptr est de fournir une classe d’abstraction de style RAII sans coût . Par conséquent, il ne devrait pas entraîner de coût autre que celui de la delete (ou de la delete[] ) de l’object alloué dynamicment. Le retard imposé par un access à une table de hachage verrouillée ou protégée, par exemple, peut cependant ê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 est en train d’écrire ici à propos de std :: weak_ptr mais pas à propos du concept de poiner faible que je pense être ce que l’auteur demande

Je pense que personne n’a mentionné pourquoi la bibliothèque standard ne fournit pas la variable_ptr pour unique_ptr. Pointeur faible CONCEPT ne nie pas l’utilisation de unique_ptr. Le pointeur faible est uniquement une information si l’object a déjà été supprimé, ce n’est donc pas un motif d’observateur généralisé, mais très simple et magique.

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éé à partir de unique_ptr existant sur un autre thread) ne sera pas détruit pendant la méthode d’appel sur un object pointu. C’est parce que faiblesse_ptr doit être cohérent avec std :: shared_ptr qui garantit la sécurité des threads. Vous pouvez implémenter la méthode faibles_ptr qui fonctionne correctement avec unique_ptr mais uniquement sur la même méthode de locking de threads 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 de pointeur faible en chrome est très probablement basé sur la destruction du dernier membre – vous devez append la fabrique en tant que dernier membre et après cela, je pense que WeakPtr est informé de la suppression d’object (je ne suis pas sûr à 100%) – si difficile à mettre en œuvre.

En général, l’utilisation de unique_ptr avec un concept de pointeur faible est OK à mon humble avis.

Il peut être utile de distinguer les raisons de préférer un unique_ptr à un shared_ptr .

Performances L’une des raisons évidentes est le temps de calcul et l’utilisation de la mémoire. Comme défini actuellement, les objects shared_ptr ont généralement besoin de quelque chose comme une valeur de compte de référence, qui prend de la place et doit également être maintenue activement. unique_ptr objects unique_ptr ne le font pas.

Sémantique Avec un unique_ptr , vous, en tant que programmeur, avez une bonne idée de la destruction de l’object pointé: lorsque l’ unique_ptr est détruit ou lorsque l’une de ses méthodes de modification est appelée. Ainsi, sur les bases de code volumineuses ou peu familières, l’utilisation de unique_ptr transmet statiquement (et applique) des informations sur le comportement d’exécution du programme qui pourraient ne pas être évidentes.

Les commentaires ci-dessus ont généralement porté sur les raisons liées à la performance selon lesquelles il ne serait pas souhaitable que les objects weak_ptr soient liés aux objects unique_ptr . Mais on peut se demander si l’argument basé sur la sémantique est une raison suffisante pour qu’une version future de la STL prenne en charge le cas d’utilisation impliqué par la question d’origine.