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 weak_ptr
travail? J'ai essayé de lire le code source de boost, et je ne connais pas assez le boost pour comprendre tout ce qu'il utilise.
Merci.
shared_ptr
utilise un objet "compteur" supplémentaire (alias "compte partagé" ou "bloc de contrôle") pour stocker le compte de référence. (BTW: cet objet "compteur" stocke également le deleter.)
Chaque shared_ptr
et weak_ptr
contient un pointeur sur la pointe réelle et un deuxième pointeur sur l'objet "compteur".
Implémenter weak_ptr
, l'objet "compteur" stocke deux compteurs différents:
shared_ptr
instances pointant vers l'objet.weak_ptr
instances pointant vers l'objet, plus une si le "nombre d'utilisation" est toujours> 0.La pointe est supprimée lorsque le "compte d'utilisation" atteint zéro.
L'objet d'assistance "compteur" est supprimé lorsque le "compte faible" atteint zéro (ce qui signifie que le "compte d'utilisation" doit également être nul, voir ci-dessus).
Lorsque vous essayez d'obtenir un shared_ptr
de weak_ptr
, la bibliothèque vérifie atomiquement le "nombre d'utilisation" et s'il est> 0 l'incrémente. Si cela réussit, vous obtenez votre shared_ptr
. Si le "nombre d'utilisation" était déjà nul, vous obtenez un shared_ptr
instance à la place.
EDIT : Maintenant, pourquoi en ajoutent-ils un au nombre faible au lieu de simplement libérer l'objet "compteur" lorsque les deux nombres tombent à zéro? Bonne question.
L'alternative serait de supprimer l'objet "compteur" lorsque le "compte d'utilisation" et le "compte faible" tombent à zéro. Voici la première raison: la vérification atomique de deux compteurs (de la taille d'un pointeur) n'est pas possible sur chaque plate-forme, et même là où elle se trouve, c'est plus compliqué que de vérifier un seul compteur.
Une autre raison est que le suppresseur doit rester valide jusqu'à ce qu'il ait terminé son exécution. Étant donné que le deleter est stocké dans l'objet "compteur", cela signifie que l'objet "compteur" doit rester valide. Considérez ce qui pourrait arriver s'il y en a un shared_ptr
et une weak_ptr
à un objet, et ils sont réinitialisés en même temps dans les threads simultanés. Disons que le shared_ptr
vient en premier. Il diminue le "nombre d'utilisation" à zéro et commence à exécuter le suppresseur. Maintenant le weak_ptr
diminue le "compte faible" à zéro et trouve que le "compte d'utilisation" est également nul. Il supprime donc l'objet "compteur", et avec lui le suppresseur. Pendant que le deleter est toujours en cours d'exécution.
Bien sûr, il y aurait différentes façons de garantir que l'objet "compteur" reste vivant, mais je pense que l'augmentation du "compte faible" par un est une solution très élégante et intuitive. Le "compte faible" devient le compte de référence pour l'objet "compteur". Et depuis shared_ptr
s font également référence au compteur, ils doivent également incrémenter le "faible nombre".
Une solution probablement encore plus intuitive serait d'incrémenter le "nombre faible" pour chaque shared_ptr
, puisque chaque shared_ptr
hold est une référence à l'objet "counter".
Ajout d'un pour tous shared_ptr
instances est juste une optimisation (enregistre un incrément/décrément atomique lors de la copie/assignation shared_ptr
les instances).