web-dev-qa-db-fra.com

Exemple d'utilisation de shared_ptr?

Bonjour, j'ai posé aujourd'hui une question à propos de Comment insérer différents types d'objets dans le même tableau de vecteurs et mon code dans cette question était 

 gate* G[1000];
G[0] = new ANDgate() ;
G[1] = new ORgate;
//gate is a class inherited by ANDgate and ORgate classes
class gate
{
 .....
 ......
 virtual void Run()
   {   //A virtual function
   }
};
class ANDgate :public gate 
  {.....
   .......
   void Run()
   {
    //AND version of Run
   }  

};
 class ORgate :public gate 
  {.....
   .......
   void Run()
   {
    //OR version of Run
   }  

};      
//Running the simulator using overloading concept
 for(...;...;..)
 {
  G[i]->Run() ;  //will run perfectly the right Run for the right Gate type
 } 

et je voulais utiliser des vecteurs alors quelqu'un a écrit que je devrais le faire:

std::vector<gate*> G;
G.Push_back(new ANDgate); 
G.Push_back(new ORgate);
for(unsigned i=0;i<G.size();++i)
{
  G[i]->Run();
}

mais alors lui et beaucoup d'autres ont suggéré que je ferais mieux d'utiliser/ Boost les conteneurs de pointeur
ou shared_ptr. J'ai passé les 3 dernières heures à lire sur ce sujet, mais la documentation me semble assez avancée. **** Quelqu'un peut-il me donner un petit exemple de code d'utilisation de shared_ptr et pourquoi il a suggéré d'utiliser shared_ptr? Il y a aussi d'autres types comme ptr_vector, ptr_list et ptr_deque ** **

Edit1: J'ai aussi lu un exemple de code contenant:

typedef boost::shared_ptr<Foo> FooPtr;
.......
int main()
{
  std::vector<FooPtr>         foo_vector;
........
FooPtr foo_ptr( new Foo( 2 ) );
  foo_vector.Push_back( foo_ptr );
...........
}

Et je ne comprends pas la syntaxe!

79
Ahmed

L'utilisation d'une vector sur shared_ptr élimine la possibilité d'une fuite de mémoire, car vous avez oublié de parcourir le vecteur et d'appeler delete sur chaque élément. Passons en revue une version légèrement modifiée de l'exemple, ligne par ligne.

typedef boost::shared_ptr<gate> gate_ptr;

Créez un alias pour le type de pointeur partagé. Cela évite la laideur dans le langage C++ qui résulte de la saisie de std::vector<boost::shared_ptr<gate> > et de l’oubli de l’espace entre les signes de fermeture plus grands que.

    std::vector<gate_ptr> vec;

Crée un vecteur vide d’objets boost::shared_ptr<gate>.

    gate_ptr ptr(new ANDgate);

Allouez une nouvelle instance ANDgate et stockez-la dans un shared_ptr. Cela est fait séparément pour éviter un problème pouvant survenir si une opération est lancée. Ce n'est pas possible dans cet exemple. Les Boost shared_ptr "Meilleures pratiques" expliquent pourquoi c’est une meilleure pratique d’affecter un objet indépendant au lieu d’un objet temporaire.

    vec.Push_back(ptr);

Cela crée un nouveau pointeur partagé dans le vecteur et y copie ptr. Le comptage des références dans les entrailles de shared_ptr garantit que l'objet alloué à l'intérieur de ptr est transféré en toute sécurité dans le vecteur.

Ce qui n'est pas expliqué, c'est que le destructeur de shared_ptr<gate> garantit que la mémoire allouée est supprimée. C'est là que la fuite de mémoire est évitée. Le destructeur de std::vector<T> s'assure que le destructeur de T est appelé pour chaque élément stocké dans le vecteur. Cependant, le destructeur d'un pointeur (par exemple, gate*) ne supprime pas la mémoire que vous avez allouée . C'est ce que vous essayez d'éviter en utilisant shared_ptr ou ptr_vector.

114
D.Shawley

J'ajouterai que l'une des choses importantes à propos de shared_ptr est de ne construire que (toujours} _ avec la syntaxe suivante:

shared_ptr<Type>(new Type(...));

De cette façon, le pointeur "réel" sur Type est anonyme pour votre portée et tenu uniquement par le pointeur partagé. Ainsi, il vous sera impossible d'utiliser accidentellement ce "vrai" pointeur. En d'autres termes, ne faites jamais ceci:

Type* t_ptr = new Type(...);
shared_ptr<Type> t_sptr ptrT(t_ptr);
//t_ptr is still hanging around!  Don't use it!

Bien que cela fonctionne, vous avez maintenant un pointeur Type* (t_ptr) dans votre fonction qui réside en dehors du pointeur partagé. Il est dangereux d’utiliser t_ptr n’importe où, car on ne sait jamais quand le pointeur partagé qui le contient risque de le détruire, et vous provoquerez une erreur de segmentation.

Il en va de même pour les pointeurs renvoyés par d'autres classes. Si une classe que vous n'avez pas écrite vous remet un pointeur, il n'est généralement pas prudent de le placer simplement dans un shared_ptr. Sauf si vous êtes sûr que la classe n'utilise plus cet objet. Parce que si vous le mettez dans un shared_ptr et que cela tombe en dehors de la portée, l'objet sera libéré lorsque la classe en aura encore besoin.

40
Ken Simon

Apprendre à utiliser des pointeurs intelligents est à mon avis l’une des étapes les plus importantes pour devenir un programmeur C++ compétent. Comme vous le savez chaque fois que vous créez un objet à un moment donné, vous souhaitez le supprimer. 

Un problème qui se pose est que, avec des exceptions, il peut être très difficile de s’assurer qu’un objet est toujours libéré une seule fois dans tous les chemins d’exécution possibles.

C'est la raison de RAII: http://en.wikipedia.org/wiki/RAII

Créer une classe d'assistance dans le but de s'assurer qu'un objet est toujours supprimé une fois dans tous les chemins d'exécution. 

Exemple de classe comme celle-ci: std :: auto_ptr

Mais parfois, vous aimez partager des objets avec d'autres. Il ne devrait être supprimé que lorsque personne ne l'utilise plus.

Afin de vous aider avec cette référence, des stratégies de comptage ont été développées mais vous devez toujours vous rappeler addref et libérer manuellement ref. C'est essentiellement le même problème que new/delete.

C'est pourquoi boost a développé boost :: shared_ptr, son pointeur intelligent de comptage de références pour vous permettre de partager des objets sans fuir par inadvertance de la mémoire.

Avec l’ajout de C++ tr1, il s’ajoute désormais à la norme c ++, mais à son nom std :: tr1 :: shared_ptr <>.

Je recommande d'utiliser le pointeur partagé standard si possible. ptr_list, ptr_dequeue et ainsi de suite sont des conteneurs spécialisés IIRC pour les types de pointeur. Je les ignore pour l'instant.

Nous pouvons donc partir de votre exemple:

std::vector<gate*> G; 
G.Push_back(new ANDgate);  
G.Push_back(new ORgate); 
for(unsigned i=0;i<G.size();++i) 
{ 
  G[i]->Run(); 
} 

Le problème ici est que maintenant, chaque fois que G sort du champ d'application, nous perdons les 2 objets ajoutés à G. Réécrivons-le pour utiliser std :: tr1 :: shared_ptr.

// Remember to include <memory> for shared_ptr
// First do an alias for std::tr1::shared_ptr<gate> so we don't have to 
// type that in every place. Call it gate_ptr. This is what typedef does.
typedef std::tr1::shared_ptr<gate> gate_ptr;    
// gate_ptr is now our "smart" pointer. So let's make a vector out of it.
std::vector<gate_ptr> G; 
// these smart_ptrs can't be implicitly created from gate* we have to be explicit about it
// gate_ptr (new ANDgate), it's a good thing:
G.Push_back(gate_ptr (new ANDgate));  
G.Push_back(gate_ptr (new ORgate)); 
for(unsigned i=0;i<G.size();++i) 
{ 
   G[i]->Run(); 
} 

Lorsque G sort de la portée, la mémoire est automatiquement récupérée.

En tant qu'exercice avec lequel je peste les nouveaux arrivants de mon équipe, je leur demande d'écrire leur propre classe de pointeurs intelligents. Ensuite, jetez la classe immédiatement et ne l'utilisez plus jamais. J'espère que vous avez acquis des connaissances cruciales sur le fonctionnement d'un pointeur intelligent. Il n'y a pas de magie vraiment.

La documentation de boost fournit un bon exemple de départ: exemple_partagé_ptr (il s’agit en fait d’un vecteur de pointeurs intelligents) ou shared_ptr doc La réponse suivante de Johannes Schaub explique la boost assez bien les pointeurs intelligents: explication des pointeurs intelligents

L'idée derrière (en aussi peu de mots que possible) ptr_vector est qu'il gère pour vous la désallocation de la mémoire derrière les pointeurs stockés: supposons que vous ayez un vecteur de pointeurs comme dans votre exemple. Lorsque vous quittez l'application ou quittez l'étendue dans laquelle le vecteur est défini, vous devrez nettoyer après vous-même (vous avez alloué dynamiquement ANDgate et ORgate), mais l'effacement du vecteur ne le fera pas car le vecteur stocke les pointeurs. et pas les objets réels (il ne détruira pas mais ce qu'il contient).

 // if you just do
 G.clear() // will clear the vector but you'll be left with 2 memory leaks
 ...
// to properly clean the vector and the objects behind it
for (std::vector<gate*>::iterator it = G.begin(); it != G.end(); it++)
{
  delete (*it);
}

boost :: ptr_vector <> se chargera de ce qui précède, ce qui signifie qu'il désallouera la mémoire derrière les pointeurs qu'il stocke.

2
celavek

Grâce à Boost, vous pouvez le faire >

std::vector<boost::any> vecobj;
    boost::shared_ptr<string> sharedString1(new string("abcdxyz!"));    
    boost::shared_ptr<int> sharedint1(new int(10));
    vecobj.Push_back(sharedString1);
    vecobj.Push_back(sharedint1);

> pour insérer un type d'objet différent dans votre conteneur de vecteurs. alors que pour y accéder, vous devez utiliser any_cast, qui fonctionne comme dynamic_cast, espère que cela fonctionnera pour vos besoins. 

2
user1808932
#include <memory>
#include <iostream>

class SharedMemory {
    public: 
        SharedMemory(int* x):_capture(x){}
        int* get() { return (_capture.get()); }
    protected:
        std::shared_ptr<int> _capture;
};

int main(int , char**){
    SharedMemory *_obj1= new SharedMemory(new int(10));
    SharedMemory *_obj2 = new SharedMemory(*_obj1);
    std::cout << " _obj1: " << *_obj1->get() << " _obj2: " << *_obj2->get()
    << std::endl;
    delete _obj2;

    std::cout << " _obj1: " << *_obj1->get() << std::endl;
    delete _obj1;
    std::cout << " done " << std::endl;
}

Ceci est un exemple de shared_ptr en action. _obj2 a été supprimé, mais le pointeur est toujours valide. La sortie est, ./test _obj1: 10 _obj2: 10 _obj2: 10 terminé 

1
Syed Raihan

Le meilleur moyen d'ajouter différents objets dans le même conteneur est d'utiliser les boucles make_shared, vector et range et vous obtiendrez un code agréable, propre et "lisible"!

typedef std::shared_ptr<gate> Ptr   
vector<Ptr> myConatiner; 
auto andGate = std::make_shared<ANDgate>();
myConatiner.Push_back(andGate );
auto orGate= std::make_shared<ORgate>();
myConatiner.Push_back(orGate);

for (auto& element : myConatiner)
    element->run();
0
Hooman