begin process at 2012 02 12 08:25:00
  Trouver un code source :
 
dans
 
Accueil > 

Code

 > 

Astuces

 > SMART POINTER

SMART POINTER


 Information sur la source

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

  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10
Catégorie :Astuces Niveau :Expert Date de création :13/02/2004 Vu :7 393

Auteur : tibur

Ecrire un message privé
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 !!!!)


 Sources du même auteur

Source avec Zip TIMER
ECRITURE / LECTURE EN C++
Source avec Zip BIG SIGNED INTEGER
DE L'IMPORTANCE DES ASSERT
STL : LA CLASSE MAP (EXEMPLE D'UN AGENDA)

 Sources de la même categorie

Source avec Zip SCHEDULER RR FIFO par yvesB87
Source avec Zip ALGORITHMES RÉCURSIFS VS ALGORITHMES ITÉRATIFS par yvesB87
Source avec Zip Source avec une capture C++ FORMAT D'IMAGE AVEC QT par pop70
Source avec une capture EXEMPLE DE POINTEURS DE FONCTION par pop70
Source avec Zip Source avec une capture [C++] CLASS REGISTER par Miwik

Commentaires et avis

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.

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 ?

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.

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

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++ !!

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.

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.

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

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

Février 2012
LMMJVSD
  12345
6789101112
13141516171819
20212223242526
272829    

Consulter la suite du CalendriCode

 
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

Google Coop CodeS-SourceS Google Coop CodeS-SourceS
Temps d'éxécution de la page : 0,499 sec (4)

Nous contacter | Annoncer sur CodeS-SourceS | Mentions légales