Exemple d'utilisation de ptr partagé?
Salut j'ai posé une question aujourd'hui à propos de Comment insérer différents types d'objets dans le même vecteur, matrice et mon code en question
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 lui et beaucoup d'autres ont suggéré que je ferais mieux d'utiliser boost conteneurs pointeur
ou shared_ptr
. J'ai passé les 3 dernières heures à lire sur ce sujet, mais la documentation me semble assez avancée . ****Peut n'importe qui me donne un petit exemple de code d'utilisation de shared_ptr
et pourquoi ils ont suggéré d'utiliser shared_ptr
. Y a - t-il aussi d'autres types comme ptr_vector
, ptr_list
et ptr_deque
** **
Edit1: j'ai aussi lu un exemple de code qui comprenait:
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!
7 réponses
L'utilisation d'un vector
de shared_ptr
supprime la possibilité de fuite de mémoire car vous avez oublié de parcourir le vecteur et d'appeler delete
sur chaque élément. Parcourons 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 taper std::vector<boost::shared_ptr<gate> >
et d'oublier l'espace entre la fermeture plus grand que les signes .
std::vector<gate_ptr> vec;
Crée un vecteur vide de boost::shared_ptr<gate>
objet.
gate_ptr ptr(new ANDgate);
Alloue une nouvelle instance ANDgate
et la stocke dans un shared_ptr
. La raison de le faire séparément est d'éviter un problème qui peut se produire si une opération lance. Ce n'est pas possible dans cet exemple. Le Boost shared_ptr
"Meilleures Pratiques" expliquer pourquoi il est un meilleures pratiques allouer dans un objet au lieu d'une mesure temporaire.
vec.push_back(ptr);
Cela crée un nouveau pointeur partagé dans le vecteur et y Copie ptr
. Référence compter 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é. C'est là que la fuite de mémoire est évitée. Le destructeur pour std::vector<T>
garantit que le destructeur pour T
est appelé pour chaque élément stocké dans le vecteur. Cependant, le destructeur pour un pointeur (par exemple, gate*
) ne supprime pas la mémoire que vous aviez alloué . C'est ce que vous essayez d'éviter en utilisant shared_ptr
ou ptr_vector
.
J'ajouterai que l'une des choses importantes à propos de shared_ptr
est de ne les construire que jamais avec la syntaxe suivante:
shared_ptr<Type>(new Type(...));
De Cette façon, la "vraie" pointeur de Type
est anonyme à votre portée, et tenues seulement par le pointeur partagé. Ainsi, il vous sera impossible d'utiliser accidentellement ce pointeur "réel". 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 Type*
pointeur (t_ptr
) dans votre fonction, qui vit à l'extérieur le pointeur partagé. Il est dangereux d'utiliser t_ptr
n'importe où, car vous ne savez jamais quand le pointeur partagé qui le contient peut le détruire, et vous allez segfault.
Il en va de même pour les pointeurs qui vous sont retourné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 sûr de le mettre dans un shared_ptr
. Pas à moins que vous ne soyez sûr que la classe n'utilise plus cet objet. Parce que si vous le mettez dans un shared_ptr
, et qu'il tombe hors de portée, l'objet obtiendra libéré quand la classe peut encore en avoir besoin.
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 voulez le supprimer.
Un problème qui se pose est qu'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'aide dans le but de s'assurer qu'un objet toujours supprimés une fois dans tous les chemins d'exécution.
Exemple d'une classe comme celle-ci: std:: auto_ptr
Mais parfois vous aimez partager des objets avec d'autres. Il ne devrait être supprimé que lorsqu'aucun ne l'utilise plus.
Afin d'aider à cela, des stratégies de comptage de références ont été développées, mais vous devez toujours vous souvenir d'addref et de release ref manuellement. En substance, c'est le même problème que new/delete.
C'est pourquoi boost s'est développé boost:: shared_ptr, c'est le pointeur intelligent de comptage de référence afin que vous puissiez partager des objets et ne pas fuir la mémoire involontairement.
Avec L'ajout de C++ tr1 ceci est maintenant ajouté à 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 sont donc des conteneurs spécialisés IIRC pour les types de pointeurs. Je les ignore pour l'instant.
Nous pouvons donc commencer à 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-ce Maintenant que chaque fois que G sort de la portée, nous fuyons 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 est hors de portée, la mémoire est automatiquement récupérée.
Comme un exercice avec lequel j'ai tourmenté les nouveaux arrivants dans mon équipe leur demande d'écrire leur propre classe de pointeur intelligent. Ensuite, après avoir terminé, jetez immédiatement la classe et ne l'utilisez plus jamais. Espérons que vous avez acquis des connaissances cruciales sur la façon dont un pointeur intelligent fonctionne sous le capot. Il n'y a pas la magie vraiment.
La documentation boost fournit un très bon exemple de démarrage: shared_ptr exemple (Il s'agit en fait d'un vecteur de pointeurs intelligents) ou shared_ptr doc La réponse suivante de Johannes Schaub explique assez bien les pointeurs intelligents boost: les pointeurs intelligents expliqués
L'idée derrière(en aussi peu de mots que possible) ptr_vector est qu'il gère la désallocation de la mémoire derrière la stockées pointeurs pour vous: disons que vous avez un vecteur de pointeurs, comme dans votre exemple. Lorsque vous quittez l'application ou quittez la portée dans laquelle le vecteur est défini, vous devrez nettoyer après vous-même(vous avez alloué dynamiquement ANDgate et ORgate) mais simplement effacer le vecteur ne le fera pas parce que le vecteur stocke les pointeurs et non 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 gérera ce qui précède pour vous-ce qui signifie qu'il va désallouer la mémoire derrière les pointeurs qu'il stocke.
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 vectoriel. alors que pour accéder, vous devez utiliser any_cast, qui fonctionne comme dynamic_cast, espère que cela fonctionnera pour vos besoins.
#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 fait
La meilleure façon d'ajouter différents objets dans le même conteneur est d'utiliser make_shared, vector et range et vous aurez 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();