Vous ne trouvez pas de réponse à votre problème ? Alors posez la question dans le forum. Souvenez-vous qu'il n'y a jamais de question bête, mais rester dans l'ignorance parce que l'on n'ose pas poser une question, ça c'est une erreur !

SMART POINTER


Information sur la source

Catégorie :Astuces Niveau : Expert Date de création : 13/02/2004 Vu : 5 364

Note :
4 / 10 - par 1 personne
4,00 / 10

  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

Commentaire sur cette source (9)
Ajouter un commentaire et/ou une note

Description

Voila comment se debarasser definitevement des pointeurs dans vos programmes C++.
Permet d'eviter tout les problemes de memoire (fuite, NULL access et acces une zone de memoire  deja deletée)

Enjoy !
 

Source

  • #ifndef SMART_POINTER
  • #define SMART_POINTER
  • #include <assert.h>
  • #include <stdlib.h>
  • #include <string>
  • class Counted {
  • public:
  • Counted() : ref_count_(0) {}
  • // a la creation 0 pointeurs referencent notre objet
  • virtual ~Counted(){assert(ref_count_==0);}
  • // on verifie que 0 pointeurs pointent sur notre objet
  • void add_ref(){ref_count_++;}
  • // ajoute une reference au compteur
  • void release_ref(){ref_count_--;}
  • // enleve une reference au compteur
  • int ref_count() const {return ref_count_;}
  • // renvoie le nombre de pointeurs pointant sur notre objet
  • private:
  • int ref_count_;
  • // le nombre de pointeurs referencant notre objet
  • };
  • template<class Nimp>
  • class SmartPointer {
  • public:
  • SmartPointer():ptr_(NULL){}
  • // constructeur vide : aucun pointeur
  • virtual ~SmartPointer(){release();}
  • // le desctructeur "relache" l'objet pointé
  • SmartPointer(const SmartPointer & sp) : ptr_(NULL){(*this) = sp;}
  • // constructeur par copie : idem que le constructeur
  • // vide suivi d'un appel à l'operateur =
  • SmartPointer(Nimp * n) : ptr_(NULL){(*this) = n;}
  • // constructeur avec un objet pointé en parametre
  • // idem que l'operateur = avec un objet en param
  • bool is_null()const{return ptr_==NULL;}
  • // test si le pointeur est NULL
  • void operator = (Nimp * n){
  • if(n==ptr_) return; // on pointe deja sur cet objet
  • release(); // on relache le ptr courant
  • acquire(n); // on recupere le nouveau pointeur
  • }
  • void operator = (const SmartPointer & sp){
  • if(this == &sp) return; // l'utilisateur fait nimporte quoi ! (p1 = p1;)
  • (*this) = sp.ptr_; // on lance l'operateur = sur l'objet
  • }
  • bool operator == (const SmartPointer & sp){return ptr_ == sp.ptr_;}
  • // test si les pointeur sont les memes
  • bool operator != (const SmartPointer<Nimp> & sp){
  • return ptr_ != sp.ptr_;
  • }
  • // test si les pointeur sont !=
  • Nimp * operator -> () const{
  • assert(ptr_!=NULL&&"Null pointer access");
  • return ptr_;
  • }
  • // simule l'operateur -> avec un petit test si le ptr est egal à NULL
  • Nimp & operator * () const{
  • assert(ptr_!=NULL&&"Null pointer access");
  • return *ptr_;
  • }
  • // simule l'operateur * avec un petit test si le ptr est egal à NULL
  • private:
  • // relache l'objet pointé
  • void release(){
  • if(ptr_==NULL) return;
  • ptr_->release_ref(); // on enleve une reference au compteur de l'objet
  • if(ptr_->ref_count()==0) // si plus aucun pointeur ne referencie l'objet
  • delete ptr_; // on le delete
  • ptr_ = NULL;
  • }
  • void acquire(Nimp * n){
  • if(ptr_!=NULL) release(); // on relache le pointeur courant
  • if(n==NULL) return;
  • ptr_ = n;
  • ptr_->add_ref(); // on ajoute une reference sur le compteur de l'objet
  • }
  • Nimp * ptr_; // l'objet pointé
  • };
  • // Petit test de notre classe SmartPointer
  • // Pour tester ca, ecrire cela dans le main :
  • // int main(int argc, _TCHAR* argv[]){
  • // Test::test();
  • // return 0;
  • // }
  • class Test : public Counted {
  • public:
  • // construit l'objet test
  • Test(const std::string & s):s_(s) {std::cout <<"constructor " <<s_ <<std::endl;}
  • // detruit l'objet test
  • ~Test(){std::cout <<"destructor " <<s_ <<std::endl;}
  • // un petit typedef pour se simplifier
  • // l'utilisation du SmartPointer de Test
  • typedef SmartPointer<Test> TestPtr;
  • static void test(){
  • // construit un nouveau ptr p1
  • std::cout <<"new p1" <<std::endl;
  • TestPtr p1 = new Test("toto");
  • // construit un nouveau ptr p2
  • std::cout <<"new p2" <<std::endl;
  • TestPtr p2 = new Test("titi");
  • // p1 et p2 pointent sur le meme objet
  • // => detruit l'ancien objet pointé par p1 (toto)
  • // car plus aucun pointeur ne referencie toto
  • std::cout <<"p1 = p2" <<std::endl;
  • p1 = p2;
  • std::cout <<"exit" <<std::endl;
  • // fin de la portee de p1 et p2
  • // les destructeur p1 et p2 sont appelés
  • // et titi est detruit
  • }
  • private:
  • std::string s_; // bahhhh
  • };
  • #endif
#ifndef SMART_POINTER
#define SMART_POINTER

#include <assert.h>
#include <stdlib.h>
#include <string>

class Counted {
 public:
  Counted() : ref_count_(0) {} 
	// a la creation 0 pointeurs referencent notre objet

  virtual ~Counted(){assert(ref_count_==0);}  
	// on verifie que 0 pointeurs pointent sur notre objet

  void add_ref(){ref_count_++;}
	// ajoute une reference au compteur

  void release_ref(){ref_count_--;}
	// enleve une reference au compteur

  int ref_count() const {return ref_count_;}
	// renvoie le nombre de pointeurs pointant sur notre objet

 private:
  int ref_count_; 
	// le nombre de pointeurs referencant notre objet
};



template<class Nimp>
class SmartPointer {
 public:

  SmartPointer():ptr_(NULL){}
	// constructeur vide : aucun pointeur

  virtual ~SmartPointer(){release();}
	// le desctructeur "relache" l'objet pointé

  SmartPointer(const SmartPointer & sp) : ptr_(NULL){(*this) = sp;}
	// constructeur par copie : idem que le constructeur
	// vide suivi d'un appel à l'operateur =

  SmartPointer(Nimp * n) : ptr_(NULL){(*this) = n;}
	// constructeur avec un objet pointé en parametre
	// idem que l'operateur = avec un objet en param

  bool is_null()const{return ptr_==NULL;}
	// test si le pointeur est NULL

  void operator = (Nimp * n){
    if(n==ptr_) return; // on pointe deja sur cet objet
    release(); // on relache le ptr courant
    acquire(n); // on recupere le nouveau pointeur
	}

  void operator = (const SmartPointer & sp){
    if(this == &sp) return; // l'utilisateur fait nimporte quoi ! (p1 = p1;)
    (*this) = sp.ptr_; // on lance l'operateur = sur l'objet
  }

  bool operator == (const SmartPointer & sp){return ptr_ == sp.ptr_;}
	// test si les pointeur sont les memes

  bool operator != (const SmartPointer<Nimp> & sp){
    return ptr_ != sp.ptr_;
  }
	// test si les pointeur sont !=

  Nimp * operator -> () const{
    assert(ptr_!=NULL&&"Null pointer access");
    return ptr_;
  }
	// simule l'operateur -> avec un petit test si le ptr est egal à NULL

  Nimp & operator * () const{
    assert(ptr_!=NULL&&"Null pointer access");
    return *ptr_;
  }
	// simule l'operateur * avec un petit test si le ptr est egal à NULL

 private:

	 // relache l'objet pointé
  void release(){
    if(ptr_==NULL) return;
    ptr_->release_ref(); // on enleve une reference au compteur de l'objet
    if(ptr_->ref_count()==0) // si plus aucun pointeur ne referencie l'objet
      delete ptr_;  // on le delete
    ptr_ = NULL;
  }
  void acquire(Nimp * n){
    if(ptr_!=NULL) release(); // on relache le pointeur courant
    if(n==NULL) return; 
    ptr_ = n; 
    ptr_->add_ref(); // on ajoute une reference sur le compteur de l'objet
  }
  Nimp * ptr_;  // l'objet pointé
};


// Petit test de notre classe SmartPointer 
// Pour tester ca, ecrire cela dans le main :

// int main(int argc, _TCHAR* argv[]){
//	Test::test();
//	return 0;
// }


class Test : public Counted {
public:
	// construit l'objet test
	Test(const std::string & s):s_(s) {std::cout <<"constructor " <<s_ <<std::endl;}

	// detruit l'objet test
	~Test(){std::cout <<"destructor " <<s_ <<std::endl;}

	// un petit typedef pour se simplifier
	// l'utilisation du SmartPointer de Test
	typedef SmartPointer<Test> TestPtr;

	static void test(){

		// construit un nouveau ptr p1
		std::cout <<"new p1" <<std::endl;
		TestPtr p1 = new Test("toto");

		// construit un nouveau ptr p2
		std::cout <<"new p2" <<std::endl;
		TestPtr p2 = new Test("titi");

		// p1 et p2 pointent sur le meme objet
		// => detruit l'ancien objet pointé par p1 (toto)
		// car plus aucun pointeur ne referencie toto
		std::cout <<"p1 = p2" <<std::endl;
		p1 = p2;

		std::cout <<"exit" <<std::endl;
		// fin de la portee de p1 et p2
		// les destructeur p1 et p2 sont appelés
		// et titi est detruit
	}

private:
	std::string s_; // bahhhh
};

#endif

Conclusion

Bon, j'ai fait des efforts : j'ai ajouté pleins de commentaires !
Petite note pour les experts : ce type de pointeurs n'est pas utilisable dans les liste doublement chainées !
imaginons la class Node :
class Node : public Counted {
public : (...)
private:
  SmartPointer<Node> prev_, next_;
}

Si vous utilisez mes SmartPointer avec ce Node, ca chiera !
Je vous laisse cogiter à l'explication...
(Faut que j'aille faire à manger !!!!)

 

Commentaires et avis

signaler à un administrateur
Commentaire de JCDjcd le 14/02/2004 08:51:36

heu excuse moi, mais moi aussi j'utilise les compteur de reference dans mes allocation, seulement ce n'est pas des classes.
Mais le probleme des liste chainees marchge tres tres bien, moi je ne voix pas pourquoi ca ne marcherai pas, et comme c'est lez cas, ce n'est pas normal.

signaler à un administrateur
Commentaire de tibur le 14/02/2004 12:36:19

Essayes le code suivant :

class List {
public:
struct Node : public Counted{
SmartPointer&lt;Node&gt; prev, next;
~Node(){nb_inst--;}
Node(){nb_inst++;}
static int nb_inst;
};
SmartPointer&lt;Node&gt; root;

static void test(){
{
List l;
l.root = new Node();

l.root-&gt;next = new Node();
l.root-&gt;next-&gt;prev = l.root;

l.root-&gt;next-&gt;next = new Node();
l.root-&gt;next-&gt;next-&gt;prev = l.root-&gt;next;

}
assert(Node::nb_inst==0);
}
};

int List::Node::nb_inst = 0;

Bon, bien, sur si tu remet à NULL tout les prev, ca revient dans l'ordre ...


Petite question, tout de meme : si c'est pas des classes, que tu utilises, c'est quoi alors ?

signaler à un administrateur
Commentaire de JCDjcd le 14/02/2004 13:53:44

bah en fait tu fais une surcouhe des malloc
a chaque fois que l'utilisateur alloue, tu coole avant sa strucutr une aute suture a toi (HEADER) qui contient les nombres de pointeues de reference, et de pluys je conserve le nom du fichier et la ligne de l'allocation, et aussi je chaine toute les allocation, comme cela a la fin de mon programme je liste toute les allocation qui n'ont pas ete allouees, et je sais lesquelles c'est !
Moi ca me debug tres bien.

signaler à un administrateur
Commentaire de BlackGoddess le 14/02/2004 16:12:31

malloc c'est du C ... ici on parle de C++ =&gt; new
sinon c pas une copie de l'auto_ptr de la STL ca des fois ? :p

signaler à un administrateur
Commentaire de JCDjcd le 14/02/2004 16:20:22

bah ... et alors ??? moi mes trucs font exatement la meme chose que ceux de cette source ! De plus ils contiennent plus d'infomration de debug, donc je ne foi pas ou est le probleme, surtout comme c'est du C, c'est protable C++ !!

signaler à un administrateur
Commentaire de tibur le 14/02/2004 20:01:39

&gt; sinon c pas une copie de l'auto_ptr de la STL ca des fois ? :p

Nan : je viens de regarder comment marche le auto_ptr. Tu n'as pas de comptage de reference.

Je te fais un copier / coller de l'operateur = entre 2 auto_ptr :

auto_ptr&lt;_Ty&gt;& operator=(auto_ptr_ref&lt;_Ty&gt;& _Right) throw ()
{ // assign compatible _Right._Ref (assume pointer)
reset(_Right._Ref.release());
return (*this);
}

Il fait un release sur le auto_ptr right. Ca veut dire que ce code plante :

typedef std::auto_ptr&lt;double&gt; DoublePtr;
DoublePtr p1(new double(5.0));
DoublePtr p2 = p1;
std::cout &lt;&lt; *p1 &lt;&lt;std::endl;

Car p1 n'est plus valide. Avec ces SmartPointer, tu peux avoir plusieurs pointeurs pointant en meme temps sur le même objet.

signaler à un administrateur
Commentaire de tibur le 14/02/2004 20:06:00

&gt; bah ... et alors ???

Je suis d'accord pour le debugging. Par contre tu ne peux pas redefinir l'operator = en C. Alors je vois pas l'interet de faire un smart pointer !

Peut etre que j'ai mal compris ta facon de faire ... Fait donc un post de ton code.

signaler à un administrateur
Commentaire de BlackGoddess le 15/02/2004 17:54:33

c'est du C, c'est protable C++ &gt;&gt; si tu savais les difficultés qu'on peut rencontrer en encapsulant du c dans du c++ ...

ok tibur :)
ds les lib boost il y a 5 smart pointers aussi pour ceux que ca interresse :)
www.boost.org

signaler à un administrateur
Commentaire de VoidSeer le 12/10/2005 14:42:25

Du classique ;-)
Pour approfondir le sujet, la meilleure référence AMA reste 'More Effective C++
de Scott Meyers, chez Addison-Wesley. Les items 28 et 29, traitent exactement de ce sujet, avec le code en prime.

Quelques remarques sur le code néanmoins :
- Quand on inclue des fichiers d'entête C, il est mieux d'utiliser ceux conçus
  pour le C++, qui encapsulent les fonctions standard dans le namespace std.
  Donc <assert.h>, et <stdlib.h> deviennent <cstdlib>, <cassert>

- On n'utilise pas NULL en C++, on utilise 0. Y'a une grande justification  
  derriere tout cela, assez technique d'ailleurs.

- Tester si un smart pointer est nul est un point délicat. L'implémentation
  proposée ici utilise une fonction membre. C'est bien, cependant cela pose
  un problème. Le Smart Pointer ne se comporte plus comme un pointeur.
  Le principe d'une bonne implémentation de Smart Pointer est qu'il puisse se
  substituer de manière transparente à un pointeur classique. Là ce n'est pas
  le cas.
  La solution proposée en général est d'implanter l'opérateur operator!.
  Ce qui permet d'écrire:
  if (!ptr) {/* Pointeur ou SmartPointer non null*/}

- Je ne vois pas l'intérêt de définir le destructeur comme virtuel. Au
  contraire. La classe n'a pas a priori vocation à être surchargée, est ne
  définir aucune fonction virtuelle fera l'économie de la création de la  
  vtable. Ce qui est plutot souhaitable pour une classe dont les instances
  se substituent à un type scalaire.

Ajouter un commentaire



Nos sponsors

Sondage...

CalendriCode

Juillet 2009
LMMJVSD
  12345
6789101112
13141516171819
20212223242526
2728293031  

Consulter la suite du CalendriCode

Comparez les prix Nouvelle version

Photothèque Nouveau !



Développement réalisé par Nicolas SOREL (Nix) avec l'aide de : Cyril DURAND et Emmanuel (EBArtSoft), Merci à Vincent pour ses précieux conseils
CodeS-SourceS.com© Toute reproduction même partielle est interdite sauf accord écrit du Webmaster
CodeS-SourceS.com© est une marque déposée tous droits réservés
Temps d'éxécution de la page : 0,546 sec

Google Coop CodeS-SourceS Google Coop CodeS-SourceS


Certaines images présentes sur le site (notament certains avatars) sont issues des collections IconShock, donc si vous souhaitez utiliser ces icons vous devez les acheter, ne les copiez pas et ne utilisez pas dans vos sites et applications sans les avoir commandé.