begin process at 2008 07 19 16:35:44
1 212 906 membres
228 nouveaux aujourd'hui
14 165 membres club

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 : 4 617

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 !!!!)
  • 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

Pub



Appels d'offres

Dessins techniques
Budget : 60€
Animation Flash - Doma...
Budget : 370€
Application flash medi...
Budget : 1 000€

CalendriCode

Juillet 2008
LMMJVSD
 123456
78910111213
14151617181920
21222324252627
28293031   

Boutique

Boutique de goodies CodeS-SourceS