Smart pointer

Soyez le premier à donner votre avis sur cette source.

Snippet vu 9 969 fois - Téléchargée 28 fois

Contenu du snippet

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 / Exemple :


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

A voir également

Ajouter un commentaire Commentaires
Messages postés
1
Date d'inscription
mercredi 12 octobre 2005
Statut
Membre
Dernière intervention
12 octobre 2005

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 , 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.
Messages postés
338
Date d'inscription
jeudi 22 août 2002
Statut
Membre
Dernière intervention
14 juin 2005

c'est du C, c'est protable C++ >> 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
Messages postés
101
Date d'inscription
samedi 9 février 2002
Statut
Membre
Dernière intervention
5 mai 2009

> 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.
Messages postés
101
Date d'inscription
samedi 9 février 2002
Statut
Membre
Dernière intervention
5 mai 2009

> 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<_Ty>& operator=(auto_ptr_ref<_Ty>& _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<double> DoublePtr;
DoublePtr p1(new double(5.0));
DoublePtr p2 = p1;
std::cout << *p1 <<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.
Messages postés
1138
Date d'inscription
mardi 10 juin 2003
Statut
Membre
Dernière intervention
25 janvier 2009
3
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++ !!
Afficher les 9 commentaires

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.