Il vous permet d'obtenir une shared_ptr
instance valide this
lorsque tout ce que vous avez est this
. Sans cela, vous n'auriez aucun moyen d'obtenir un accès shared_ptr
à this
moins que vous n'en ayez déjà un en tant que membre. Cet exemple de la documentation de boost pour enable_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 valeur valide shared_ptr
, même si elle n'avait pas d'instance membre. Notez que vous ne pouvez pas simplement faire ceci:
class Y: public enable_shared_from_this<Y>
{
public:
shared_ptr<Y> f()
{
return shared_ptr<Y>(this);
}
}
Le pointeur partagé que cela a renvoyé aura un nombre de références différent du "bon", et l'un d'eux finira par perdre et conserver une référence pendant lorsque l'objet est supprimé.
enable_shared_from_this
est devenu une partie de la norme C ++ 11. Vous pouvez également l'obtenir à partir de là ainsi que de boost.
1800 INFORMATION
la source
std::shared_ptr
constructeur sur un pointeur brut s'il hérite destd::enable_shared_from_this
. Je ne sais pas si la sémantique de Boost a été mise à jour pour supporter cela.std::shared_ptr
pour un objet qui est déjà géré par un autrestd::shared_ptr
ne consultera pas la référence faible stockée en interne et entraînera donc un comportement indéfini." ( en.cppreference.com/w/cpp/memory/enable_shared_from_this )shared_ptr<Y> q = p
?std::make_shared<T>
.d'après l'article du Dr Dobbs sur les pointeurs 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:
Aucun des deux
shared_ptr
objets ne connaît l'autre, donc les deux essaieront de libérer la ressource quand ils seront détruits. Cela entraîne généralement des problèmes.De même, si une fonction membre a besoin d'un
shared_ptr
objet qui possède l'objet auquel elle est appelée, elle ne peut pas simplement créer un objet à la volée:Ce code a le même problème que l'exemple précédent, bien que sous une forme plus subtile. Lorsqu'il est construit, l'
shared_pt
objet rsp1
possède la nouvelle ressource allouée. Le code à l'intérieur de la fonction membreS::dangerous
ne connaît pas cetshared_ptr
objet, donc l'shared_ptr
objet qu'il renvoie est distinct desp1
. Copier le nouvelshared_ptr
objet danssp2
n'aide pas; quandsp2
sort de la portée, il libérera la ressource, et quandsp1
sortira de la portée, il relâchera la ressource.Pour éviter ce problème, utilisez le modèle de classe
enable_shared_from_this
. Le modèle prend un argument de type de modèle, qui est le nom de la classe qui définit la ressource gérée. Cette classe doit, à son tour, être dérivée publiquement du modèle; comme ça:Lorsque vous effectuez cette opération, gardez à l'esprit que l'objet sur lequel vous appelez
shared_from_this
doit appartenir à unshared_ptr
objet. Cela ne fonctionnera pas:la source
shared_ptr<S> sp1(new S);
cela, il peut être préférable d'utilisershared_ptr<S> sp1 = make_shared<S>();
, voir par exemple stackoverflow.com/questions/18301511/…shared_ptr<S> sp2 = p->not_dangerous();
car l'écueil ici est que vous devez créer un shared_ptr de la manière normale avant d'appelershared_from_this()
la première fois! C'est vraiment facile de se tromper! Avant C ++ 17, il est UB d'appelershared_from_this()
avant qu'exactement un shared_ptr ait été créé de façon normale:auto sptr = std::make_shared<S>();
oushared_ptr<S> sptr(new S());
. Heureusement, à partir de C ++ 17, cela le fera.S* s = new S(); shared_ptr<S> ptr = s->not_dangerous();
<- Il est permis d'appeler shared_from_this uniquement sur un objet précédemment partagé, c'est-à-dire sur un objet géré par std :: shared_ptr <T>. Sinon, le comportement n'est pas défini (jusqu'à C ++ 17) std :: bad_weak_ptr est levé (par le constructeur shared_ptr à partir d'une faiblesse construite par défaut) (depuis C ++ 17). . Donc, la réalité est qu'il faut l'appeleralways_dangerous()
, car vous avez besoin de savoir si elle a déjà été partagée ou non.Voici mon explication, du point de vue des écrous et boulons (la réponse du haut n'a pas «cliqué» avec moi). * Notez que cela est le résultat de la recherche de la source de shared_ptr et enable_shared_from_this fourni avec Visual Studio 2012. Peut-être que d'autres compilateurs implémentent enable_shared_from_this différemment ... *
enable_shared_from_this<T>
ajoute uneweak_ptr<T>
instance privée àT
laquelle détient le « un vrai nombre de références » pour l'instance deT
.Ainsi, lorsque vous créez pour la première fois un
shared_ptr<T>
sur un nouveau T *, le faiblesse_ptr interne de ce T * est initialisé avec un décompte de 1. Le nouveau revientshared_ptr
essentiellement sur ce pointweak_ptr
.T
peut alors, dans ses méthodes, appelershared_from_this
pour obtenir une instance deshared_ptr<T>
celle-ci sur le même compte de référence stocké en interne . De cette façon, vous avez toujours un endroit oùT*
le décompte de ref est stocké plutôt que d'avoir plusieursshared_ptr
instances qui ne se connaissent pas, et chacun penseshared_ptr
que c'est lui qui est responsable du décompteT
et de le supprimer lorsque leur ref -le nombre atteint zéro.la source
So, when you first create...
parce que c'est une exigence (comme vous le dites, le faiblesse_ptr n'est pas initialisé jusqu'à ce que vous passiez le pointeur des objets dans un ctor shared_ptr!) Et cette exigence est l'endroit où les choses peuvent aller horriblement mal si vous êtes pas prudent. Si vous ne créez aucun shared_ptr avant d'appeler,shared_from_this
vous obtenez UB - de même si vous créez plus d'un shared_ptr, vous obtenez également UB. Vous devez en quelque sorte vous assurer de créer un shared_ptr exactement une fois.enable_shared_from_this
est fragile au départ, car il s'agit de pouvoir obtenir unshared_ptr<T>
de aT*
, mais en réalité, lorsque vous obtenez un pointeur,T* t
il n'est généralement pas sûr de supposer que quelque chose est déjà partagé ou non, et faire la mauvaise supposition est UB.Notez que l'utilisation d'un boost :: intrusive_ptr ne souffre pas de ce problème. C'est souvent un moyen plus pratique de contourner ce problème.
la source
enable_shared_from_this
vous permet de travailler avec une API qui accepte spécifiquementshared_ptr<>
. À mon avis, une telle API est généralement Doing It Wrong (car il vaut mieux laisser quelque chose de plus élevé dans la pile posséder la mémoire) mais si vous êtes obligé de travailler avec une telle API, c'est une bonne option.C'est exactement la même chose en c ++ 11 et versions ultérieures: c'est pour permettre la possibilité de revenir en
this
tant que pointeur partagé car celathis
vous donne un pointeur brut.en d'autres termes, cela vous permet de transformer du code comme celui-ci
en cela:
la source
shared_ptr
. Vous voudrez peut-être changer l'interface pour vous assurer que c'est le cas.std::shared_ptr<Node> getParent const()
, je l'exposerais normalement comme à laNodePtr getParent const()
place. Si vous avez absolument besoin d'accéder au pointeur brut interne (meilleur exemple: traiter avec une bibliothèque C), il y en astd::shared_ptr<T>::get
pour cela, que je déteste mentionner parce que j'ai cet accesseur de pointeur brut utilisé trop de fois pour la mauvaise raison.Une autre façon consiste à ajouter un
weak_ptr<Y> m_stub
membre dans leclass Y
. Puis écrire: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)
, il est valide même pendant un constructeur.C'est OK s'il y a plus de stubs comme celui-ci dans la hiérarchie d'héritage, cela n'empêchera pas la destruction de l'objet.
Edit: Comme l'a correctement souligné l'utilisateur nobar, le code détruirait l'objet Y lorsque l'affectation est terminée et les variables temporaires sont détruites. Par conséquent, ma réponse est incorrecte.
la source
shared_ptr<>
qui ne supprime pas sa pointe, c'est exagéré. Vous pouvez simplement direreturn shared_ptr<Y>(this, no_op_deleter);
oùno_op_deleter
un objet de fonction unaire prendY*
et ne fait rien.m_stub = shared_ptr<Y>(this)
va construire et détruire immédiatement un shared_ptr temporaire à partir de cela. Lorsque cette instruction est terminée,this
sera supprimée et toutes les références suivantes seront pendantes.enable_shared_from_this
, il conserve unweak_ptr
de lui-même (rempli par le ctor), retourné comme unshared_ptr
lorsque vous appelezshared_from_this
. En d'autres termes, vous dupliquez ceenable_shared_from_this
qui fournit déjà.