Quand est-ce que std:: faible ptr est utile?

j'ai commencé à étudier les pointeurs intelligents de C++11 et je ne vois pas d'utilisation utile de std::weak_ptr . Est-ce que quelqu'un peut me dire quand std::weak_ptr est utile/nécessaire?

183
demandé sur curiousguy 2012-08-20 03:00:05
la source

11 ответов

Un bon exemple serait un cache.

pour les objets récemment consultés, vous voulez les garder en mémoire, donc vous tenez un pointeur fort vers eux. Périodiquement, vous scannez le cache et décidez quels objets n'ont pas été consultés récemment. Vous n'avez pas besoin de les garder en mémoire, donc vous vous débarrassez du pointeur fort.

mais que se passe-t-il si cet objet est utilisé et qu'un autre code contient un pointeur fort? Si le cache se débarrasse de son seul pointeur l'objet, il ne peut jamais trouver de nouveau. Ainsi, le cache garde un pointeur faible vers les objets qu'il doit trouver s'ils restent dans la mémoire.

c'est exactement ce qu'un pointeur faible fait -- il vous permet de localiser un objet s'il est toujours autour, mais ne le garde pas autour si rien d'autre n'en a besoin.

159
répondu David Schwartz 2012-08-20 03:06:28
la source

std::weak_ptr est un très bon moyen de résoudre le problème pointeur pendulaire . Simplement en utilisant des pointeurs il est impossible de savoir si les données référencées a été libéré ou non. Au lieu de cela, en laissant un std::shared_ptr Gérer les données, et en fournissant std::weak_ptr aux utilisateurs des données, les utilisateurs peuvent vérifier la validité des données en appelant expired() ou lock() .

vous ne pouvez pas faire cela avec std::shared_ptr seul, parce que tous Les instances std::shared_ptr partagent la propriété des données qui ne sont pas supprimées avant que toutes les instances std::shared_ptr soient supprimées. Voici un exemple de la façon de vérifier pour l'aiguille pendante en utilisant lock() :

#include <iostream>
#include <memory>

int main()
{
    // OLD, problem with dangling pointer
    // PROBLEM: ref will point to undefined data!

    int* ptr = new int(10);
    int* ref = ptr;
    delete ptr;

    // NEW
    // SOLUTION: check expired() or lock() to determine if pointer is valid

    // empty definition
    std::shared_ptr<int> sptr;

    // takes ownership of pointer
    sptr.reset(new int);
    *sptr = 10;

    // get pointer to data without taking ownership
    std::weak_ptr<int> weak1 = sptr;

    // deletes managed object, acquires new pointer
    sptr.reset(new int);
    *sptr = 5;

    // get pointer to new data without taking ownership
    std::weak_ptr<int> weak2 = sptr;

    // weak1 is expired!
    if(auto tmp = weak1.lock())
        std::cout << *tmp << '\n';
    else
        std::cout << "weak1 is expired\n";

    // weak2 points to new data (5)
    if(auto tmp = weak2.lock())
        std::cout << *tmp << '\n';
    else
        std::cout << "weak2 is expired\n";
}
210
répondu sunefred 2018-03-21 00:19:05
la source

une autre réponse, peut-être plus simple. (pour les autres googlers)

supposons que vous ayez des objets Team et Member .

évidemment c'est une relation : l'objet Team aura des pointeurs vers son Members . Et il est probable que les membres auront aussi un pointeur arrière vers leur objet Team .

alors vous avez un cycle de dépendance. Si vous utilisez shared_ptr , les objets ne seront plus automatiquement libéré lorsque vous abandonnez la référence sur eux, parce qu'ils se réfèrent l'un l'autre d'une manière cyclique. C'est une fuite de mémoire.

vous cassez ceci en utilisant weak_ptr . Le" propriétaire "utilise habituellement shared_ptr et le" propriétaire "utilise weak_ptr pour sa société mère, et le convertit Temporairement en shared_ptr lorsqu'il a besoin d'avoir accès à sa société mère.

stocker un ptr faible:

weak_ptr<Parent> parentWeakPtr_ = parentSharedPtr; // automatic conversion to weak from shared

alors utilisez en cas de besoin

shared_ptr<Parent> tempParentSharedPtr = parentWeakPtr_.lock(); // on the stack, from the weak ptr
if( not tempParentSharedPtr ) {
  // yes it may failed if parent was freed since we stored weak_ptr
} else {
  // do stuff
}
// tempParentSharedPtr is released when it goes out of scope
84
répondu Offirmo 2015-08-09 14:44:15
la source

voici un exemple, que m'a donné @jleahy: supposons que vous ayez un ensemble de tâches, exécutées asynchrones, et gérées par un std::shared_ptr<Task> . Vous pouvez vouloir faire quelque chose avec ces tâches périodiquement, ainsi un événement de minuterie peut traverser un std::vector<std::weak_ptr<Task>> et donner les tâches quelque chose à faire. Cependant, simultanément une tâche peut avoir simultanément décidé qu'il n'est plus nécessaire et mourir. Le timer peut ainsi vérifier si la tâche est toujours en vie en faisant un pointeur partagé de la faible pointer et utiliser ce pointeur partagé, à condition qu'il ne soit pas null.

17
répondu Kerrek SB 2012-08-20 03:07:41
la source

weak_ptr est également bon pour vérifier la suppression correcte d'un objet - en particulier dans les tests unitaires. Cas d'utilisation typique pourrait ressembler à ceci:

std::weak_ptr<X> weak_x{ shared_x };
shared_x.reset();
BOOST_CHECK(weak_x.lock());
... //do something that should remove all other copies of shared_x and hence destroy x
BOOST_CHECK(!weak_x.lock());
12
répondu Cookie 2014-06-05 16:55:53
la source

shared_ptr : contient l'objet réel.

weak_ptr : utilise lock pour se connecter au propriétaire réel ou renvoie NULL autrement.

weak ptr

en gros, weak_ptr rôle est similaire au rôle de agence de logement . Sans agents, pour avoir une maison à louer nous avons peut-être vérifier aléatoire maisons de la ville. Les agents s'assurent que nous ne visitons que les maisons qui sont encore accessibles et disponibles à louer.

9
répondu Saurav Sahu 2017-05-03 10:33:49
la source

ils sont utiles avec Boost.Asio lorsque vous n'êtes pas garanti qu'un objet existe toujours quand un gestionnaire asynchrone est invoquée. L'astuce consiste à attacher un weak_ptr dans l'objet asynchone handler, en utilisant des captures std::bind ou lambda.

void MyClass::startTimer()
{
    std::weak_ptr<MyClass> weak = shared_from_this();
    timer_.async_wait( [weak](const boost::system::error_code& ec)
    {
        auto self = weak.lock();
        if (self)
        {
            self->handleTimeout();
        }
        else
        {
            std::cout << "Target object no longer exists!\n";
        }
    } );
}

c'est une variante de l'idiome self = shared_from_this() souvent vu dans Boost.Par exemple, un manipulateur asynchrone en attente de traitement et non prolongera la durée de vie de la cible. objet, pourtant, est encore sûr si l'objet cible est supprimé.

9
répondu Emile Cormier 2018-08-09 17:26:42
la source

lors de l'utilisation de pointeurs, il est important de comprendre les différents types de pointeurs disponibles et quand il est logique d'utiliser chacun d'eux. Il y a quatre types de pointeurs dans deux catégories comme suit:

  • pointeurs bruts:
    • pointeur brut [i.e. SomeClass* ptrToSomeClass = new SomeClass(); ]
  • pointeurs intelligents:
    • pointeurs uniques [i.e. std::unique_ptr<SomeClass> uniquePtrToSomeClass ( new SomeClass() ); ]
    • Pointeurs partagés [i.e. std::shared_ptr<SomeClass> sharedPtrToSomeClass ( new SomeClass() ); ]
    • points faibles [i.e. std::weak_ptr<SomeClass> weakPtrToSomeWeakOrSharedPtr ( weakOrSharedPtr ); ]

pointeurs bruts (parfois appelés "pointeurs hérités", ou "pointeurs C") fournissent le comportement de pointeur "bare-bones" et sont une source commune de bugs et de fuites de mémoire. Les pointeurs Raw ne fournissent aucun moyen de garder une trace de la propriété de la ressource et les développeurs doivent appeler' delete ' manuellement pour s'assurer qu'ils ne créent pas une mémoire fuite. Cela devient difficile si la ressource est partagée, car il peut être difficile de savoir si des objets pointent toujours vers la ressource. Pour ces raisons, les indicateurs bruts devraient généralement être évités et utilisés uniquement dans les sections critiques du code avec une portée limitée.

pointeurs uniques sont un pointeur intelligent de base qui "possède" le pointeur brut sous-jacent à la ressource et est responsable de l'appel supprimer et libérer la mémoire allouée une fois l'objet que "possède" le pointeur unique sort de la portée. Le nom "unique" fait référence au fait qu'un seul objet peut "posséder" le pointeur unique à un moment donné. La propriété peut être transférée à un autre objet via la commande move, mais un pointeur unique ne peut jamais être copié ou partagé. Pour ces raisons, les pointeurs uniques sont une bonne alternative aux pointeurs bruts dans le cas où un seul objet a besoin du pointeur à un moment donné, et cela allège le développeur de la nécessité de libérer la mémoire à la fin de le cycle de vie de l'objet propriétaire.

pointeurs partagés sont un autre type de pointeur intelligent qui sont similaires à des pointeurs uniques, mais permettent à de nombreux objets d'avoir la propriété sur le pointeur partagé. Comme le pointeur unique, les pointeurs partagés sont responsables de libérer la mémoire allouée une fois que tous les objets pointent vers la ressource. Pour ce faire, il utilise une technique appelée comptage de référence. Chaque fois qu'un nouvel objet prend possession du pointeur partagé le compte de référence est incrémenté de un. De même, lorsqu'un objet sort de sa portée ou cesse de pointer vers la ressource, le nombre de référence est décrémenté d'un. Lorsque le compteur de référence atteint zéro, la mémoire allouée est libéré. Pour ces raisons, les pointeurs partagés sont un type très puissant de pointeur intelligent qui devrait être utilisé chaque fois que plusieurs objets ont besoin de pointer vers la même ressource.

enfin, les pointeurs faibles sont un autre type de pointeur intelligent qui, plutôt que de pointer vers une ressource directement, ils pointent vers un autre pointeur (faible ou partagé). Les pointeurs faibles ne peuvent pas accéder directement à un objet, mais ils peuvent dire si l'objet existe toujours ou s'il est expiré. Un pointeur faible peut être temporairement converti en pointeur intelligent pour accéder à l'objet pointé (à condition qu'il existe toujours). À titre d'exemple, prenons l'exemple suivant:

  • Vous êtes occupé et le chevauchement des réunions: Une Réunion de Réunion et B
  • vous décidez d'aller à La Réunion A et votre collègue va à La Réunion b
  • vous dites à votre collègue que si la Réunion B est toujours en cours après la fin de la réunion a, vous vous joindrez
  • Les deux scénarios suivants pourraient jouer:
    • la réunion a se termine et La Réunion B est toujours en cours, donc vous rejoignez
    • la réunion a se termine et La Réunion B est aussi terminée, donc vous ne Rejoignez pas

dans la par exemple, vous avez un pointeur faible à La Réunion B. Vous n'êtes pas un "propriétaire" dans la Réunion B de sorte qu'elle peut se terminer sans vous, et vous ne savez pas si elle s'est terminée ou non à moins que vous vérifiez. Si cela n'est pas terminé, vous pouvez vous joindre et participer, sinon, vous ne le pouvez pas. C'est différent d'avoir un indicateur partagé pour la Réunion B parce que vous seriez alors un "propriétaire" à la fois à La Réunion A et à La Réunion B (participer aux deux en même temps).

l'exemple illustre comment un pointeur faible fonctionne et est utile quand un objet doit être un extérieur observateur , mais ne veut pas la responsabilité de la propriété. Cela est particulièrement utile dans le cas où deux objets doivent se diriger l'un vers l'autre (A. K. A. une référence circulaire). Avec des pointeurs partagés, aucun des objets ne peut être libéré parce qu'ils sont encore "fortement" pointés par l'autre objet. Avec des pointeurs faibles, les objets peuvent être consulté en cas de besoin, et libéré quand ils n'ont plus besoin d'exister.

7
répondu Jeremy 2017-12-08 06:52:16
la source

http://en.cppreference.com/w/cpp/memory/weak_ptr std:: weak_ptr est un pointeur intelligent qui contient une référence non propriétaire ("faible") à un objet géré par std::shared_ptr. Il doit être converti à std::shared_ptr pour accéder à l'objet référencé.

std::weak modèles de propriété temporaire: lorsqu'un objet doit être accessible seulement si il existe, et il peut être supprimé à tout moment par quelqu'un d'autre, std::weak est utilisé pour suivi de l'objet, et il s'est converti à std::shared_ptr à assumer la propriété temporaire. Si la norme std::shared_ptr originale est détruite à ce moment, la durée de vie de l'objet est prolongée jusqu'à ce que la norme std::shared_ptr temporaire soit également détruite.

en outre, std::weak_ptr est utilisé pour briser les références circulaires de std::shared_ptr.

1
répondu MYLOGOS 2014-05-12 15:57:47
la source

Il y a un inconvénient de pointeur partagé: shared_pointer ne peut pas gérer la dépendance du cycle parent-enfant. Signifie que si la classe parent utilise l'objet de la classe enfant à l'aide d'un pointeur partagé, dans le même fichier, si l'enfant utilise de l'objet de la classe parente. Le pointeur partagé ne réussira pas à détruire tous les objets, même le pointeur partagé n'appelle pas du tout le destructeur dans le scénario de dépendance de cycle. fondamentalement pointeur partagé ne supporte pas le mécanisme de comptage de référence.

cet inconvénient, nous pouvons le surmonter en utilisant weak_pointer.

1
répondu ashutosh 2018-07-02 22:05:07
la source

lorsque nous ne voulons pas posséder l'objet:

Ex:

class A
{
    shared_ptr<int> sPtr1;
    weak_ptr<int> wPtr1;
}

dans la classe ci-dessus wPtr1 ne possède pas la ressource pointée par wPtr1. Si la ressource est supprimée alors wPtr1 est expiré.

pour éviter la dépendance circulaire:

shard_ptr<A> <----| shared_ptr<B> <------
    ^             |          ^          |
    |             |          |          |
    |             |          |          |
    |             |          |          |
    |             |          |          |
class A           |     class B         |
    |             |          |          |
    |             ------------          |
    |                                   |
    -------------------------------------

maintenant si nous faisons le shared_ptr de la Classe B et A, l'use_count des deux pointeurs est deux.

lorsque le shared_ptr sort du champ d'application, le nombre reste 1 et par conséquent l'objet A et B ne sera pas supprimé.

class B;

class A
{
    shared_ptr<B> sP1; // use weak_ptr instead to avoid CD

public:
    A() {  cout << "A()" << endl; }
    ~A() { cout << "~A()" << endl; }

    void setShared(shared_ptr<B>& p)
    {
        sP1 = p;
    }
};

class B
{
    shared_ptr<A> sP1;

public:
    B() {  cout << "B()" << endl; }
    ~B() { cout << "~B()" << endl; }

    void setShared(shared_ptr<A>& p)
    {
        sP1 = p;
    }
};

int main()
{
    shared_ptr<A> aPtr(new A);
    shared_ptr<B> bPtr(new B);

    aPtr->setShared(bPtr);
    bPtr->setShared(aPtr);

    return 0;  
}

sortie:

A()
B()

comme nous pouvons voir de la sortie que A et b pointer ne sont jamais supprimés et donc fuite de mémoire.

pour éviter un tel problème il suffit d'utiliser weak_ptr dans la classe A au lieu de shared_ptr ce qui a plus de sens.

0
répondu Swapnil 2018-08-12 11:04:16
la source

Autres questions sur c++ c++11 smart-pointers shared-ptr weak-ptr