OVH Cloud OVH Cloud

Classe smart pointer maison

1 réponse
Avatar
Aurelien Regat-Barrel
Bonjour,
Jusque là j'utilise boost::shared_ptr, toujours de cette manière:

class A;
typedef boost::shared_ptr<A> APtr;

class A
{
public:
static APtr New()
{
return APtr( new A );
}

protected:
A() {}

// ...
};

APtr a = A::New();
// ...

Mais c'est un peu lourd à écrire à chaque fois. J'essaye de capitaliser
tout ça dans une classe smart ptr template à moi, qui s'utiliserait ainsi:

class A : PtrBase<A>
{
// ...
};

A::ptr a = A::New();

A ce sujet je suis étonné de ne pas trouver de classe smart ptr qui
utilise une base dont il faut hériter. Celà règle le problème de loger
quelque part le compteur partagé. Enfin, à mon avis.

Donc PtrBase contient la gestion du compteur, et définit le type ptr. Ca
ressemble à ça:

// déclaration de la classe smart pointeur
template<typename T>
class Ptr;

// la classe dont on hérite et qui sert à stocker
// le compteur
template<typename T>
class PtrBase
{
public:
typedef Ptr<T> ptr;

static Ptr<T> New()
{
return new T;
}

template<typename P1>
static Ptr<T> New( P1 p1 )
{
return new T( p1 );
}


protected:
PtrBase() : count( 0 ) {}
virtual ~PtrBase() {}

private:
// donner accès à la classe smart ptr pour qu'elle
// gère le compteur
friend class Ptr<T>;
int count;
};

// implémentation de la classe smart pointeur
template<typename T>
class Ptr
{
public:
Ptr() : ptr( 0 ) {}

Ptr( T * t ) :
ptr( t )
{
++this->ptr->count;
}

Ptr( const Ptr<T> & p ) :
ptr( p.ptr )
{
++this->ptr->count;
}

~Ptr()
{
if ( this->ptr &&
--this->ptr->count == 0 )
{
delete this->ptr;
}
}

T * operator ->()
{
assert( this->ptr );
return this->ptr;
}

void reset()
{
if ( this->ptr )
{
--this->ptr->count;
this->ptr = 0;
}
}

private:
T *ptr;
};

Exemple d'utilisation:

class A : public PtrBase<A>
{
protected:
friend PtrBase<A>; // nécessaire...

A( int )
{
cout << "A::A()\n";
}

public:
~A()
{
cout << "A::~A()\n";
}

public:
int value;
};

int main()
{
A::ptr a1 = A::New( 10 );
A::ptr a2 = a1;
a1->value = 0;
a1.reset();
cout << a2->value << '\n';
}

Qu'en pensez-vous ? Voyez-vous mieux ?
Celà existe-t-il déjà ?

Merci.

--
Aurélien Regat-Barrel

1 réponse

Avatar
kanze
Aurelien Regat-Barrel wrote:

A ce sujet je suis étonné de ne pas trouver de classe smart
ptr qui utilise une base dont il faut hériter.


Tu n'as pas lu l'essentiel. Voir Scott Meyers, "More Effective
C++", items 28 et 29. Je suis sûr qu'il y en a d'autres.

Voir auissi la bibliothèque OSE (http://ose.sourceforge.net/),
surtout le chapître sur « Resource Management » dans le
« Library Manual ». Ou Gabi::RefCntPtr -- ce n'est pas encore
officiel, mais si tu vas à gabisoft.free.fr, et cherche les
liens vers le code, tu dois pouvoir le trouver. (Ce n'est pas
encore officiel parce que la documentation qui décrit le code ne
correspond pas à ce qu'il y a réelement là, il y a sans doute
des liens qui ne marche pas, etc., etc. Je suis encore en train
de tester l'utilisation de lftp, et de mettre à jour les pages
déscriptives par rapport à ce que j'ai modifié dans le code.)

Celà règle le problème de loger quelque part le compteur
partagé. Enfin, à mon avis.


C'est mon avis aussi.

Ça a le désavantage que tu ne peux pas faire des pointeurs vers
des objets pré-existants, du genre ofstream. Personnellement, je
n'ai pas trouvé ça un désavantage dans la pratique, mais pour
certains, c'est éliminatoire.

Le gros avantage, c'est que je peux construire un RefCntPtr à
partir d'un pointeur brut autant de fois que je veux. Et les
pointeurs bruts, tu ne les évites pas -- puisque this est un
pointeur brut. (Boost a des choses spéciales pour le cas de
this. Mais il faut y penser, et s'en servir ; avec le compteur
embarqué dans l'objet, ça marche automatiquement.)

Donc PtrBase contient la gestion du compteur, et définit le
type ptr. Ca ressemble à ça:

// déclaration de la classe smart pointeur
template<typename T>
class Ptr;

// la classe dont on hérite et qui sert à stocker
// le compteur
template<typename T>
class PtrBase
{
public:
typedef Ptr<T> ptr;

static Ptr<T> New()
{
return new T;
}

template<typename P1>
static Ptr<T> New( P1 p1 )
{
return new T( p1 );
}


Et si T::T() prend deux paramètres ? Trois ?

Si on travaille uniquement avec les Ptr<Base>, or que Base est
une classe abstraite, et qu'il y a plusieurs classes dérivées
différentes ? (C'est souvent le cas chez moi.)

Dans l'ensemble : la dérivation de Gabi::RefCntObj est public.
Elle fait donc partie de l'interface ; l'utilisateur le sait, et
donc sait à quoi se tenir. Jusqu'ici, ça n'a jamais posé de
problème.

Qu'en pensez-vous ? Voyez-vous mieux ?
Celà existe-t-il déjà ?


Voir ci-dessus : OSE ou Gabi Lib. Et sans doute bien d'autres,
vue que c'est ce que conseille Scott Meyers.

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34