J'ai couru à travers enable_shared_from_this
en lisant les exemples de Boost.Asio et après avoir lu la documentation, je suis encore perdu pour savoir comment cela devrait être utilisé correctement. Quelqu'un peut-il me donner s'il vous plaît un exemple et/ou une explication de l'utilisation de cette classe est logique.
Il vous permet d’obtenir une instance valide shared_ptr
À this
alors que tout ce que vous avez est this
. Sans cela, vous n'auriez aucun moyen d'obtenir un shared_ptr
Vers this
, à moins d'en avoir déjà un en tant que membre. Cet exemple tiré 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 un shared_ptr
Valide, même si elle n'a 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é renvoyé aura un nombre de références différent de celui "correct" et l'un d'eux finira par perdre et conserver une référence en suspens lorsque l'objet sera 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.
de 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:
int *ip = new int;
shared_ptr<int> sp1(ip);
shared_ptr<int> sp2(ip);
Aucun des deux shared_ptr
Les objets connaissent l'autre, alors les deux essaient de libérer la ressource lorsqu'ils sont détruits. Cela conduit 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 sur lequel il est appelé, il ne peut pas simplement 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, mais sous une forme plus subtile. Quand il est construit, le shared_pt
r objet sp1
possède la nouvelle ressource allouée. Le code à l'intérieur de la fonction membre S::dangerous
ne sait pas à ce sujet shared_ptr
objet, donc le shared_ptr
l'objet renvoyé est distinct de sp1
. Copier le nouveau shared_ptr
objet à sp2
n'aide pas; quand sp2
sort du cadre, la ressource sera libérée, et quand sp1
sort du cadre, la ressource sera libérée à nouveau.
Le moyen d'éviter ce problème consiste à utiliser le modèle de classe enable_shared_from_this
. Le modèle prend un argument de type 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:
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 appartenir à un shared_ptr
objet. Cela ne fonctionnera pas:
int main()
{
S *p = new S;
shared_ptr<S> sp2 = p->not_dangerous(); // don't do this
}
Voici mon explication, du point de vue des détails (la réponse du haut n’a pas "cliqué" avec moi). * Notez que ceci est le résultat d'une recherche de la source pour 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 une instance privée weak_ptr<T>
À T
qui contient le 'n compte de références vraies' pour l'instance de T
.
Ainsi, lorsque vous créez pour la première fois un shared_ptr<T>
Sur un nouveau T *, le fichier faible_ptr interne de ce dernier est initialisé avec un nombre de référence égal à 1. Le nouveau shared_ptr
Est essentiellement basé sur ce weak_ptr
.
T
peut alors, dans ses méthodes, appeler shared_from_this
pour obtenir une instance de shared_ptr<T>
qui retourne sur le même nombre de références stockées en interne. De cette façon, vous avez toujours un endroit où le nombre de références de T*
Est stocké au lieu d'avoir plusieurs instances shared_ptr
Qui ne se connaissent pas et chacune pense qu'elles sont les shared_ptr
Responsable du comptage en parallèle T
et de le supprimer lorsque leur compte en référence atteint zéro.
Notez que l'utilisation d'un boost :: intrusive_ptr ne souffre pas de ce problème. C'est souvent un moyen plus pratique de résoudre ce problème.
C'est exactement la même chose dans c ++ 11 et les versions ultérieures: cela permet d'activer la possibilité de retourner this
en tant que pointeur partagé puisque this
vous donne un pointeur brut.
dans d'autres mots, il vous permet de transformer 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;
};