Comment fonctionne un RTR faible?

je comprends comment utiliser weak_ptr et shared_ptr . Je comprends comment shared_ptr fonctionne, en comptant le nombre de références dans son objet. Comment fonctionne weak_ptr ? J'ai essayé de lire le code source de boost, et je ne suis pas assez familier avec boost pour comprendre toutes les choses qu'il utilise.

Merci.

55
demandé sur Saurav Sahu 2011-04-15 04:35:40
la source

2 ответов

shared_ptr utilise un objet supplémentaire appelé "counter" (alias. "partagé comte" ou "bloc de contrôle") pour stocker le nombre de références. (BTW: cet objet "counter" stocke aussi le deleter.)

Tous "les 151900920" et weak_ptr contient un pointeur vers le réel pointee, et un deuxième pointeur vers le "compteur" de l'objet.

pour implémenter weak_ptr , l'objet" counter "stocke deux compteurs différents:

  • le "use count "est le nombre d'instances shared_ptr pointant vers l'objet.
  • le "nombre faible" est le nombre d'instances weak_ptr pointant vers l'objet, plus un si le "nombre d'utilisation" est toujours > 0.

La pointee est supprimé lorsque le "nombre d'utilisations" atteint zéro.

l'objet "counter" helper "est supprimé lorsque le" weak count "atteint zéro (ce qui signifie que le "use count" doit aussi être zéro, voir ci-dessus).

lorsque vous essayez d'obtenir un shared_ptr à partir d'un weak_ptr , la bibliothèque vérifie atomiquement le "nombre d'utilisation", et s'il est > 0 incréments il. Si cela réussit, vous obtenez votre shared_ptr . Si le " compte d'utilisation "était déjà zéro, vous obtenez une instance vide shared_ptr à la place.


modifier : maintenant, pourquoi en ajoutent - ils un au compte faible au lieu de simplement libérer l'objet "counter" quand les deux comptes tomber à zéro? Bonne question.

l'autre solution consisterait à supprimer l'objet" counter "lorsque le" use count "et le" weak count " tombent à zéro. Voici la première raison: vérifier atomiquement deux compteurs (pointeur de taille) n'est pas possible sur toutes les plateformes, et même là où c'est, c'est plus compliqué que de vérifier un seul compteur.

une autre raison est que le deleter doit rester valide jusqu'à ce qu'il ait terminé l'exécution. Puisque le deleter est stocké dans l'objet" counter", ce qui signifie que l'objet" counter " doit rester valide. Considérez ce qui pourrait arriver s'il y a un shared_ptr et un weak_ptr pour un objet, et ils sont réinitialisés en même temps dans les threads concurrents. Disons que le shared_ptr vient en premier. Il diminue le "compte d'utilisation" à zéro, et commence à exécuter le deleter. Maintenant le weak_ptr réduit le" compte faible " à zéro, et trouve que le "compte d'utilisation" est égal à zéro. Donc il supprime l'objet "counter", et avec lui la deleter. Pendant que le deleter court toujours.

bien sûr, il y aurait différentes façons de s'assurer que l'objet "compteur" reste vivant, mais je pense que l'augmentation du "faible compte" par un est une solution très élégante et intuitive. Le " nombre faible "devient le nombre de référence pour l'objet" compteur". Et puisque shared_ptr s référence le contre-objet aussi, ils doivent aussi augmenter le "faible compte".

a probablement encore plus intuitif la solution serait d'augmenter le " nombre faible "pour chaque shared_ptr , puisque chaque shared_ptr hold est une référence à l'objet" counter".

Ajouter un pour toutes les instances shared_ptr n'est qu'une optimisation (sauvegarde un incrément/décrément atomique lors de la copie/assignation des instances shared_ptr ).

88
répondu Paul Groke 2017-03-08 19:10:23
la source

fondamentalement, un" weak_ptr "est un pointeur ordinaire" T* "qui vous permet de récupérer une référence forte, i.e." shared_ptr", plus loin dans le code.

tout comme un T* ordinaire, le weak_ptr ne fait aucun comptage de référence. En interne, pour supporter la référence-en comptant sur un type arbitraire T, la STL (ou toute autre bibliothèque implémentant ce genre de logique) crée un objet wrapper que nous appellerons "Anchor". "Anchor" existe uniquement pour mettre en œuvre le compte de référence et "lorsque le compte est zéro, appeler delete" comportement dont nous avons besoin.

dans une référence forte, shared_ptr implémente sa copie, operator=, constructor, destructor, et autres API pertinentes pour mettre à jour le compte de référence de"Anchor". C'est comment un shared_ptr garantit que votre "T" vies pour exactement aussi longtemps que quelqu'un l'utilise. Dans un "weak_ptr", ces mêmes API ne font que copier L'ancre actuelle ptr. Ils ne mettent pas à jour les comptes de référence.

c'est pourquoi les APIs les plus importants de "weak_ptr "est" expiré "et le mal nommé"lock". "Périmé "vous dit si l'objet sous - jacent est toujours là-c'est-à-dire" S'est-il déjà effacé parce que toutes les références solides sont sorties du champ d'application?". "Lock" convertira (s'il le peut) le weak_ptr en shared_ptr de référence forte, en rétablissant le comptage de référence.

BTW," lock " est un nom terrible pour cette API. Vous n'invoquez pas (seulement) un mutex, vous créez une référence forte à partir d'une référence faible, avec cette "ancre"" agir. Le plus grand défaut dans les deux modèles est qu'ils n'ont pas implémenté l'opérateur ->, donc pour faire quoi que ce soit avec votre objet, vous devez récupérer le "T*"brut. Ils ont fait cela principalement pour supporter des choses comme "shared_ptr", parce que les types primitifs ne supportent pas l'opérateur" ->".

-6
répondu user3726672 2014-07-31 22:59:49
la source

Autres questions sur c++ boost weak-references tr1 weak-ptr