Quelle est l'utilité de `enable_shared_from_this`?
-
23-08-2019 - |
Question
Je couru à travers enable_shared_from_this
en lisant les exemples Boost.Asio et après avoir lu la documentation que je suis encore perdu de la façon dont cela devrait être utilisé correctement. Quelqu'un peut-il s'il vous plaît me donner un exemple et / ou explication et lors de l'utilisation de cette classe est logique.
La solution
Il vous permet d'obtenir une instance de shared_ptr
valide pour this
, quand tout ce que vous avez est this
. Sans elle, vous auriez aucun moyen d'obtenir un shared_ptr
à this
, à moins que vous aviez déjà un en tant que membre. Cet exemple de la documentation 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 () retourne un shared_ptr
valable, même si elle avait pas d'instance de membre. Notez que vous ne pouvez pas faire 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 du « bon » l'un, et l'un d'entre eux finissent par perdre et la tenue d'une référence boiteuse lorsque l'objet est supprimé.
enable_shared_from_this
fait partie de la norme 11 C ++. Vous pouvez également l'obtenir à partir de là, ainsi que de stimuler.
Autres conseils
de l'article Dr Dobbs sur les pointeurs faibles, je pense que cet exemple est plus facile à comprendre (source: http: // drdobbs .com / cpp / 184402026 ):
... code comme cela ne fonctionne pas correctement:
int *ip = new int;
shared_ptr<int> sp1(ip);
shared_ptr<int> sp2(ip);
Aucune des deux objets shared_ptr
connaît de l'autre, de sorte que les deux vont essayer de libérer la ressource quand ils sont détruits. Cela conduit généralement à des problèmes.
De même, si une fonction membre a besoin d'un objet shared_ptr
qui est propriétaire de l'objet qu'il est appelé à, il peut non seulement 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. Quand il est construit, l'objet shared_pt
r sp1
est propriétaire de la ressource nouvellement allouée. Le code dans la fonction membre S::dangerous
ne connaît pas cet objet shared_ptr
, de sorte que l'objet shared_ptr
qu'il retourne est distinct de sp1
. Copie du nouvel objet shared_ptr
à sp2
ne contribue pas; quand sp2
est hors de portée, il libérera la ressource, et quand sp1
est hors de portée, il publiera à nouveau la ressource.
La façon d'éviter ce problème est d'utiliser 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é publiquement à partir du modèle; comme ceci:
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;
}
Quand vous faites cela, gardez à l'esprit que l'objet sur lequel vous appelez shared_from_this
doit appartenir à un objet shared_ptr
. Cela ne fonctionnera pas:
int main()
{
S *p = new S;
shared_ptr<S> sp2 = p->not_dangerous(); // don't do this
}
Voici mon explication, d'un point de vue Boulonnerie (top réponse n'a pas « clic » avec moi). * Notez que ceci est le résultat d'une enquête sur la source de shared_ptr et enable_shared_from_this qui vient avec Visual Studio 2012. Peut-être que d'autres compilateurs différemment enable_shared_from_this ... mettre en œuvre *
enable_shared_from_this<T>
ajoute une instance de weak_ptr<T>
privé à T
qui détient le une vraie référence count pour l'instance de T
.
Alors, lorsque vous créez un shared_ptr<T>
sur un nouveau T *, que weak_ptr interne de T * s'initialisé avec un refcount 1. La nouvelle shared_ptr
soutient essentiellement sur ce weak_ptr
.
T
peut alors, dans ses méthodes, appeler shared_from_this
pour obtenir une instance de shared_ptr<T>
que dos sur le même nombre de référence mémorisé en interne . De cette façon, vous avez toujours un endroit où ref-comte de T*
est stocké plutôt que d'avoir plusieurs instances de shared_ptr
qui ne connaissent pas les uns les autres, et chacun pense qu'ils sont les shared_ptr
qui est en charge de T
ref-comptage et la suppression lorsque leur ref-compteur atteint zéro.
Notez que l'utilisation d'un boost :: intrusive_ptr ne souffre pas de ce problème. Cela est souvent un moyen plus pratique pour contourner ce problème.
Il est exactement la même en c ++ 11 et plus tard. Il est de permettre la possibilité de revenir this
comme pointeur partagé depuis this
vous donne un pointeur brut
en d'autres termes, il vous permet d'activer 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;
};
Une autre façon est d'ajouter un membre de weak_ptr<Y> m_stub
dans le class Y
. Ensuite, écrivez:
shared_ptr<Y> Y::f()
{
return m_stub.lock();
}
utile lorsque vous ne pouvez pas changer la classe que vous dérivez de (par exemple étendre la bibliothèque d'autres personnes). Ne pas oublier d'initialiser le membre, par exemple par m_stub = shared_ptr<Y>(this)
, son est valide même au cours d'un constructeur.
Il est OK s'il y a plus de talons comme celui-ci dans la hiérarchie d'héritage, il ne sera pas empêcher la destruction de l'objet.
Modifier Comme correctement souligné Nobar utilisateur, le code de détruire un objet Y lorsque l'affectation est terminée et les variables temporaires sont détruits. Par conséquent, ma réponse est incorrecte.