Je lis le livre de Scott Meyers "Effective C++". Il a été mentionné qu'il existe tr1::shared_ptr
et tr1::weak_ptr
agissent comme des pointeurs intégrés, mais ils gardent une trace du nombre tr1::shared_ptrs
pointe sur un objet.
C'est ce qu'on appelle le comptage des références. Cela fonctionne bien pour empêcher les fuites de ressources dans les structures de données acycliques, mais si deux ou plusieurs objets contiennent tr1::shared_ptrs
de telle sorte qu'un cycle se forme, le cycle peut maintenir le compte de référence de l'autre au-dessus de zéro, même lorsque tous les pointeurs externes au cycle ont été détruits.
C'est là que tr1::weak_ptrs
entrez.
Ma question est de savoir comment les structures de données cycliques font le compte de référence au-dessus de zéro. Je demande un exemple de programme C++. Comment le problème est-il résolu par weak_ptrs
? (encore une fois, avec exemple s'il vous plaît).
UNE shared_ptr
enveloppe un mécanisme de comptage de références autour d'un pointeur brut. Donc, pour chaque instance de shared_ptr
le nombre de références est augmenté d'une unité. Si deux share_ptr
les objets se réfèrent l'un à l'autre, ils ne seront jamais supprimés car ils ne se retrouveront jamais avec un compte de référence de zéro.
weak_ptr
pointe vers un shared_ptr
mais n'augmente pas son nombre de références. Cela signifie que l'objet sous-jacent peut toujours être supprimé même s'il existe un weak_ptr
référence à celui-ci.
La façon dont cela fonctionne est que le weak_ptr
peut être utilisé pour créer un shared_ptr
pour chaque fois que l'on veut utiliser l'objet sous-jacent. Si toutefois l'objet a déjà été supprimé, une instance vide d'un shared_ptr
est retourné. Étant donné que le nombre de références sur l'objet sous-jacent n'est pas augmenté avec un weak_ptr
référence, une référence circulaire n'entraînera pas la suppression de l'objet sous-jacent.
Permettez-moi de répéter votre question: "Ma question, comment les structures de données cycliques font le nombre de références au-dessus de zéro, veuillez demander à montrer avec exemple dans le programme C++. Comment le problème est résolu par weak_ptrs
encore avec un exemple s'il vous plaît. "
Le problème se produit avec du code C++ comme ceci (conceptuellement):
class A { shared_ptr<B> b; ... };
class B { shared_ptr<A> a; ... };
shared_ptr<A> x(new A); // +1
x->b = new B; // +1
x->b->a = x; // +1
// Ref count of 'x' is 2.
// Ref count of 'x->b' is 1.
// When 'x' leaves the scope, there will be a memory leak:
// 2 is decremented to 1, and so both ref counts will be 1.
// (Memory is deallocated only when ref count drops to 0)
Pour répondre à la deuxième partie de votre question: Il est mathématiquement impossible pour le comptage de références de traiter les cycles. Par conséquent, un weak_ptr
(qui est essentiellement une version simplifiée de shared_ptr
) ne peut pas être utilisé pour résoudre le problème de cycle - le programmeur résout le problème de cycle.
Pour le résoudre, le programmeur doit être conscient de la relation propriété entre les objets, ou doit inventer une relation de propriété si une telle propriété n'existe pas naturellement.
Le code C++ ci-dessus peut être modifié pour que A possède B:
class A { shared_ptr<B> b; ... };
class B { weak_ptr<A> a; ... };
shared_ptr<A> x(new A); // +1
x->b = new B; // +1
x->b->a = x; // No +1 here
// Ref count of 'x' is 1.
// Ref count of 'x->b' is 1.
// When 'x' leaves the scope, its ref count will drop to 0.
// While destroying it, ref count of 'x->b' will drop to 0.
// So both A and B will be deallocated.
Une question cruciale est: Can weak_ptr
être utilisé dans le cas où le programmeur ne peut pas dire la relation de propriété et ne peut établir aucune propriété statique en raison d'un manque de privilège ou d'un manque d'informations?
La réponse est: si la propriété des objets n'est pas claire, weak_ptr
ne peut pas aide. S'il y a un cycle, le programmeur doit le trouver et le casser. Un autre remède consiste à utiliser un langage de programmation avec récupération de place complète (comme: Java, C #, Go, Haskell), ou à utiliser un récupérateur de place conservateur (= imparfait) qui fonctionne avec C/C++ (tel que: Boehm GC) .
Pour les futurs lecteurs.
Je veux juste souligner que l'explication donnée par Atom est excellent, voici le code de travail
#include <memory> // and others
using namespace std;
class B; // forward declaration
// for clarity, add explicit destructor to see that they are not called
class A { public: shared_ptr<B> b; ~A() {cout << "~A()" << endl; } };
class B { public: shared_ptr<A> a; ~B() {cout << "~B()" << endl; } };
shared_ptr<A> x(new A); //x->b share_ptr is default initialized
x->b = make_shared<B>(); // you can't do "= new B" on shared_ptr
x->b->a = x;
cout << x.use_count() << endl;
Les pointeurs faibles "observent" simplement l'objet géré; ils ne le "maintiennent pas en vie" ni n'affectent sa durée de vie. Contrairement à shared_ptr
, Lorsque le dernier weak_ptr
Est hors de portée ou disparaît, l'objet pointé peut toujours exister car le weak_ptr
N'affecte pas la durée de vie de l'objet - il n'a aucun droit de propriété. Le weak_ptr
Peut être utilisé pour déterminer si l'objet existe et pour fournir un shared_ptr
Qui peut être utilisé pour s'y référer.
La définition de weak_ptr
Est conçue pour le rendre relativement infaillible, donc en conséquence, vous ne pouvez pas faire grand chose directement avec un weak_ptr
. Par exemple, vous ne pouvez pas le déréférencer; ni operator*
ni operator->
n'est défini pour un weak_ptr
. Vous ne pouvez pas accéder au pointeur sur l'objet avec lui - il n'y a pas de fonction get()
. Il existe une fonction de comparaison définie pour que vous puissiez stocker weak_ptrs
Dans un conteneur commandé, mais c'est tout.