Quelle est l'utilité de "permettre partagé à partir de cette"?

j'ai couru à travers enable_shared_from_this en lisant le Boost.Asio exemples et après avoir lu la documentation je suis encore perdu pour la façon dont cela devrait être correctement utilisé. Quelqu'un peut-il me donner un exemple et/ou explication lors de l'utilisation de cette classe de sens.

287
demandé sur Terry Jan Reedy 2009-04-03 05:46:07

6 réponses

il vous permet d'obtenir une instance valide shared_ptr à this , quand tout ce que vous avez est this . Sans elle , vous n'auriez aucun moyen d'obtenir un shared_ptr à this , sauf si vous en aviez déjà un comme membre. Cet exemple tiré de la documentation boost pour enabl_shared_from_this :

class Y: public enable_shared_from_this<Y>
{
public:

    shared_ptr<Y> f()
    {
        return shared_from_this();
    }
}

int main()
{
    shared_ptr<Y> p(new Y);
    shared_ptr<Y> q = p->f();
    assert(p == q);
    assert(!(p < q || q < p)); // p and q must share ownership
}

la méthode f() renvoie une shared_ptr valide , même si elle n'a pas d'instance de membre. Notez que vous ne pouvez pas tout simplement ceci:

class Y: public enable_shared_from_this<Y>
{
public:

    shared_ptr<Y> f()
    {
        return shared_ptr<Y>(this);
    }
}

le pointeur partagé que ce retour aura un compte de référence différent de celui" correct", et l'un d'eux finira par perdre et tenir une référence pendante lorsque l'objet est supprimé.

enable_shared_from_this fait désormais partie de la norme C++ 11. Vous pouvez également l'obtenir à partir de là, ainsi que de boost.

302
répondu 1800 INFORMATION 2018-03-19 01:59:45

de L'article du Dr Dobbs sur les points faibles, je pense que cet exemple est plus facile à comprendre (source: http://drdobbs.com/cpp/184402026 ):

...un code comme celui-ci ne fonctionnera pas correctement:

int *ip = new int;
shared_ptr<int> sp1(ip);
shared_ptr<int> sp2(ip);

aucun des deux shared_ptr objets ne connaît l'autre, donc les deux vont essayer de libérer la ressource quand ils sont détruits. Qui conduit généralement à des problèmes.

fonction membre a besoin d'un shared_ptr objet propriétaire de l'objet qu'on l'appelle, il ne peut pas créer un objet à la volée:

struct S
{
  shared_ptr<S> dangerous()
  {
     return shared_ptr<S>(this);   // don't do this!
  }
};

int main()
{
   shared_ptr<S> sp1(new S);
   shared_ptr<S> sp2 = sp1->dangerous();
   return 0;
}

Ce code a le même problème que l'exemple précédent, bien que sous une forme plus subtile. Lorsqu'elle est construite, l'objet shared_pt R sp1 est propriétaire de la ressource nouvellement attribuée. Le code à l'intérieur de la fonction membre S::dangerous ne sait pas à propos de cet "objet 151940920", donc l'objet shared_ptr qu'il le retour est à distinguer de la sp1 . Copier le nouvel objet shared_ptr vers sp2 n'aide pas; quand sp2 sort de la portée, il libère la ressource, et quand sp1 sort de la portée, il libère la ressource à nouveau.

La manière d'éviter ce problème est d'utiliser le modèle de classe enable_shared_from_this . Le template prend un argument template type, qui est le nom de la classe qui définit la ressource gérée. Cette classe doit, à son tour,,

struct S : enable_shared_from_this<S>
{
  shared_ptr<S> not_dangerous()
  {
    return shared_from_this();
  }
};

int main()
{
   shared_ptr<S> sp1(new S);
   shared_ptr<S> sp2 = sp1->not_dangerous();
   return 0;
}

Lorsque vous faites cela, gardez à l'esprit que l'objet sur lequel vous appelez shared_from_this doit être possédé par un shared_ptr objet. Cela ne marchera pas:

int main()
{
   S *p = new S;
   shared_ptr<S> sp2 = p->not_dangerous();     // don't do this
}
159
répondu Artashes Aghajanyan 2016-12-12 12:30:47

Voici mon explication, du point de vue des écrous et des boulons (la réponse du haut n'a pas "cliqué" avec moi). * Notez que ceci est le résultat d'une enquête sur la source de shared_ptr et enable_shared_from_this fourni avec Visual Studio 2012. Peut-être que d'autres compilateurs implémentent enabl_shared_f_this différemment...*

enable_shared_from_this<T> ajoute une instance privée weak_ptr<T> à T qui détient le " un véritable compte de référence pour le instance de T .

Ainsi, lorsque vous créez pour la première fois un shared_ptr<T> sur un nouveau T*, que le fichier weak_ptr interne de T*est initialisé avec un refcount de 1. Le nouveau shared_ptr revient en gros sur ce weak_ptr .

T peut alors, dans ses méthodes, appeler shared_from_this pour obtenir une instance de shared_ptr<T> que retourne sur le même nombre de référence stocké en interne . De cette façon, vous avez toujours une place où T* 's ref-count est stocké plutôt que d'avoir plusieurs shared_ptr instances qui ne savent pas l'un de l'autre, et chacun pense qu'ils sont le shared_ptr qui est en charge de la ref-counting T et de le supprimer lorsque leur ref-count atteint zéro.

23
répondu mackenir 2012-08-01 12:34:07

Notez que l'utilisation d'un boost::intrusive_ptr ne souffre pas de ce problème. C'est souvent une façon plus pratique de contourner cette question.

3
répondu blais 2012-06-13 13:25:40

c'est exactement la même chose en c++11 et plus tard: c'est pour permettre la possibilité de retourner this comme pointeur partagé puisque this vous donne un pointeur brut.

en d'autres termes, il vous permet de tourner le code comme ceci

class Node {
public:
    Node* getParent const() {
        if (m_parent) {
            return m_parent;
        } else {
            return this;
        }
    }

private:

    Node * m_parent = nullptr;
};           

dans ceci:

class Node : std::enable_shared_from_this<Node> {
public:
    std::shared_ptr<Node> getParent const() {
        std::shared_ptr<Node> parent = m_parent.lock();
        if (parent) {
            return parent;
        } else {
            return shared_from_this();
        }
    }

private:

    std::weak_ptr<Node> m_parent;
};           
2
répondu mchiasson 2017-11-15 21:58:48

une autre façon consiste à ajouter un weak_ptr<Y> m_stub dans le class Y . Alors écrivez:

shared_ptr<Y> Y::f()
{
    return m_stub.lock();
}

utile lorsque vous ne pouvez pas changer la classe dont vous dérivez (par exemple étendre la bibliothèque d'autres personnes). N'oubliez pas d'initialiser le membre, par exemple par m_stub = shared_ptr<Y>(this) , son est valide même pendant un constructeur.

S'il y a plus de talons comme celui-ci dans la hiérarchie des héritages, cela n'empêchera pas la destruction de l'objet.

Edit: comme l'utilisateur nobar l'a correctement souligné, le code détruirait L'objet Y lorsque l'affectation est terminée et les variables temporaires sont détruites. Donc ma réponse est incorrecte.

-2
répondu PetrH 2011-03-28 15:38:35