web-dev-qa-db-fra.com

différences shared_ptr et faibles_ptr

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).

69
venkysmarty

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.

47
doron

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_ptrne 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) .

108
user811773

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;  
17
newprint

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.

4
peterDriscoll