OVH Cloud OVH Cloud

du bon usage des membres statics...

10 réponses
Avatar
Stan
Salut,

J'ai créé une classe de base
du style
class cmd{
public:
cmd();
virtual ~cmd();
virtual void set();
virtual void get();
};

dont hérite d'autres classes plus spécifiques...

Je souhaiterai avoir un historique de
chaque commande créée ( de façon statique ou dynamique )
durant l'execution de l'application.

J'avais pensé à un vector comme par ex :
class cmd{
public:
static vector<cmd*> histo;

cmd( );
virtual ~cmd( );
virtual void set( );
virtual void get( );
virtual void PrintInfos( );
};

qui serait actualisé par le conctructeur de la classe de base
genre cmd::cmd() { histo.push_back( this ) };

et de manière approprié dans le destructeur.

Conceptuellement, cette approche vous semble-t-elle bonne ?


Par contre, en l'essayant, j'ai rencontré un pb étonnant :

Depuis une fonction je veux faire :
void GestionHisto( )
{
cmd* t=0;

for( int i=0; i < cmd::histo.size( ) ; i++)
{
t = cmd::histo[i];
if( t ) f->PrintInfos( )
}
}

Et là, ça ne marche pas, size( ) revoie tjours 0, alors
qu'une trace dans le constructeur de la classe de base
m'indique que size( ) s'incrémente bien à chaque création
d'une commande.

Un avis sur la question ?

--

Stan .

10 réponses

Avatar
Fabien LE LEZ
On Wed, 9 Feb 2005 12:04:09 +0100, "Stan" <(remove 1,2,3)
:

class cmd{
public:
static vector<cmd*> histo;


Pas cool.
D'une part, seules des fonctions de cmd ont le droit de modifier
histo ; ce ne doit donc en aucun cas être un membre public.
D'autre part, si une variable représente un historique, nomme-la
historique. Pas histo ni histogramme.

Autre petit détail : est-ce que "histo" doit réellement être membre de
la classe ?

Je verrais plus une variable "static" libre, limitée au seul module où
les fonctions membres de "cmd" sont définies.

Du style :

// .h
class cmd
{
public:
typedef std::vector<cmd*> HistoriqueCommandes;
static HistoriqueCommandes const& cmd::GetHistorique();
};


// .cpp
static HistoriqueCommandes historique_des_commandes;

HistoriqueCommandes const& cmd::GetHistorique()
{
return historique_des_commandes;
}

Au fait, est-ce que tu dois modifier les "cmd" pointés dans le code
qui utilise ce tableau ?
Si non, std::vector <cmd const*> conviendrait mieux.

--
;-)

Avatar
nico
static vector<cmd*> histo;

genre cmd::cmd() { histo.push_back( this ) };


Salut,

Si c'est pour un historique, je pense que stocker des pointeurs n'est pas
une bonne idée.
Il faudrait plutot stocker par valeur.
Et est-ce vraiment nécessaire de stocker carrément la classe ? Car je ne
sais pas ce que tu veux en faire exactement mais moi je stockerai plutot un
std::string décrivant la classe...


--
nico

Avatar
Stan
"Fabien LE LEZ" a écrit dans le message de
news:
| On Wed, 9 Feb 2005 12:04:09 +0100, "Stan" <(remove 1,2,3)
| :
|
| >class cmd{
| >public:
| > static vector<cmd*> histo;
|
| Pas cool.
| D'une part, seules des fonctions de cmd ont le droit de modifier
| histo ; ce ne doit donc en aucun cas être un membre public.

Oui c'est exact, j'avais l'intention de modifier
ça après avoir résollu le problème que j'ai évoqué ( voir + bas ).

| D'autre part, si une variable représente un historique, nomme-la
| historique. Pas histo ni histogramme.

Il est évident que j'ai fourni un exemple, sans vouloir
te vexer, je ne cherchais pas une leçon de vocabulaire ;-)

|
| Autre petit détail : est-ce que "histo" doit réellement être membre de
| la classe ?

Ma prérogative initiale est d'assurer un historique quelque soit le mode
de création de la commande; donc, je cherche la méthode la plus canonique
qui soit.


| Je verrais plus une variable "static" libre, limitée au seul module où
| les fonctions membres de "cmd" sont définies.
|
| Du style :
|
| // .h
| class cmd
| {
| public:
| typedef std::vector<cmd*> HistoriqueCommandes;
| static HistoriqueCommandes const& cmd::GetHistorique();
| };
|
|
| // .cpp
| static HistoriqueCommandes historique_des_commandes;
|
| HistoriqueCommandes const& cmd::GetHistorique()
| {
| return historique_des_commandes;
| }
|
| Au fait, est-ce que tu dois modifier les "cmd" pointés dans le code
| qui utilise ce tableau ?
| Si non, std::vector <cmd const*> conviendrait mieux.
|

C'est exact, c'est ce que je vais utiliser, même si ce n'est pas ce que
j'ai mis dans mon exemple.

Par contre, j'ai toujours le pb suivant :
Apparemment, dans le constructeur mon vector se rempli bien ( size
s'incrémente bien),
mais la fonction GetHistorique me retourne un vector qui est vide.

Où est le problème ?

--

Stan .
Avatar
Olivier Azeau
Stan (remove 1,2,3) wrote:
Par contre, j'ai toujours le pb suivant :
Apparemment, dans le constructeur mon vector se rempli bien ( size
s'incrémente bien),
mais la fonction GetHistorique me retourne un vector qui est vide.

Où est le problème ?


J'envisagerais 3 pistes :
(1) les éléments du vecteur ont été retirés
(2) tu ne regardes pas sur le meme objet vecteur
(3) tu as une corruption mémoire quelque part

Avatar
Dimitri PAPADOPOULOS-ORFANOS
Si c'est pour un historique, je pense que stocker des pointeurs n'est pas
une bonne idée.
Il faudrait plutot stocker par valeur.
Et est-ce vraiment nécessaire de stocker carrément la classe ? Car je ne
sais pas ce que tu veux en faire exactement mais moi je stockerai plutot un
std::string décrivant la classe...


C'est bien la classe qu'il faut stocker. Voir par exemple le design
pattern "Command".

Dimitri

Avatar
kanze
Stan (remove 1,2,3) wrote:

J'ai créé une classe de base du style

class cmd{
public:
cmd();
virtual ~cmd();
virtual void set();
virtual void get();
};

dont hérite d'autres classes plus spécifiques...

Je souhaiterai avoir un historique de chaque commande créée (
de façon statique ou dynamique ) durant l'execution de
l'application.

J'avais pensé à un vector comme par ex :
class cmd{
public:
static vector<cmd*> histo;

cmd( );
virtual ~cmd( );
virtual void set( );
virtual void get( );
virtual void PrintInfos( );
};

qui serait actualisé par le conctructeur de la classe de base
genre cmd::cmd() { histo.push_back( this ) };

et de manière approprié dans le destructeur.

Conceptuellement, cette approche vous semble-t-elle bonne ?


En gros. Je l'utilise souvent. Deux problèmes cependant :

D'abord, tu parles d'une « historique ». Est-ce une historique,
où simplement une liste des objets qui existe à un moment
donné@? Parce que si c'est une historique, tu ne peux pas
stocker des pointeurs à des objets si l'objet sera détruit sans
que le pointeur soit enlevé de la collection. Et si on enlève
les pointeurs, ce n'est plus une historique, mais simplement un
régistre.

En fait, c'est juste pour savoir ce que tu essaies à faire.

Deuxièmement, s'il va pouvoir exister des instances statiques
des commandes, il risque d'avoir un problème de l'ordre de
construction, et éventuellement aussi de durée de vie. Si toutes
les commandes se trouvent dans la même unité de compilation, et
tu écris :

std::vector< cmd* > cmd::histo ;
DerivedCommand1 cmd1 ;
DerivedCommand2 cmd2 ;

il n'y aurait pas de problème. Mais dès que tu définis une
commande dans une autre unité de compilation, l'ordre de
construction n'est pas garantie. (Et en fait, les symptomes que
tu décris pourrait bien provenir du fait que le cmd::histo est
construit après toutes les commandes.)

La solution classique ici, c'est d'emballer l'objet avec l'histo
dans un singleton, une fonction statique qui en renvoie une
référence à l'objet, en se chargeant de le construire au premier
appel, un peu du genre :

std::vector< cmd* >&
cmd::histo() // fonction membre statique !
{
static std::vector< cmd* >* result
= new std::vector< cmd* > ;
return *result ;
}

(N'oublie pas le lock si tu es en multi-thread.)

Par contre, en l'essayant, j'ai rencontré un pb étonnant :

Depuis une fonction je veux faire :
void GestionHisto( )
{
cmd* t=0;

for( int i=0; i < cmd::histo.size( ) ; i++)
{
t = cmd::histo[i];
if( t ) f->PrintInfos( )
}
}

Et là, ça ne marche pas, size( ) revoie tjours 0, alors qu'une
trace dans le constructeur de la classe de base m'indique que
size( ) s'incrémente bien à chaque création d'une commande.

Un avis sur la question ?


Je parie que toutes tes commandes sont des objets statiques,
définis dans des sources autres que la source qui contient la
définition de cmd::histo (ou avant cette définition dans la même
source).

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

Avatar
Blue_Bear
Stan wrote:



Salut,

J'ai créé une classe de base
du style
class cmd{
public:
cmd();
virtual ~cmd();
virtual void set();
virtual void get();
};

dont hérite d'autres classes plus spécifiques...

Je souhaiterai avoir un historique de
chaque commande créée ( de façon statique ou dynamique )
durant l'execution de l'application.

J'avais pensé à un vector comme par ex :
class cmd{
public:
static vector<cmd*> histo;

cmd( );
virtual ~cmd( );
virtual void set( );
virtual void get( );
virtual void PrintInfos( );
};

qui serait actualisé par le conctructeur de la classe de base
genre cmd::cmd() { histo.push_back( this ) };

et de manière approprié dans le destructeur.

Conceptuellement, cette approche vous semble-t-elle bonne ?


Par contre, en l'essayant, j'ai rencontré un pb étonnant :

Depuis une fonction je veux faire :
void GestionHisto( )
{
cmd* t=0;

for( int i=0; i < cmd::histo.size( ) ; i++)
{
t = cmd::histo[i];
if( t ) f->PrintInfos( )
}
}



t.histo.size() ça ne serrait pas plus approprié que cmd::histo.size( )?
de meme tu doit puvoir utiliser directement t.histo[i];
parce que la je pense que ca utilise une nouvelle instance de classe non?

Avatar
Stan
a écrit dans le message de
news:

Conceptuellement, cette approche vous semble-t-elle bonne ?


En gros. Je l'utilise souvent. Deux problèmes cependant :

D'abord, tu parles d'une « historique ». Est-ce une historique,
où simplement une liste des objets qui existe à un moment
donné@? Parce que si c'est une historique, tu ne peux pas
stocker des pointeurs à des objets si l'objet sera détruit sans


Même si c'est le destructeur du dit objet qui s'en charge ?

que le pointeur soit enlevé de la collection. Et si on enlève
les pointeurs, ce n'est plus une historique, mais simplement un
régistre.

En fait, c'est juste pour savoir ce que tu essaies à faire.


Effectivement, c'est plus un registre qu'un historique.

[...]

La solution classique ici, c'est d'emballer l'objet avec l'histo
dans un singleton, une fonction statique qui en renvoie une
référence à l'objet, en se chargeant de le construire au premier
appel, un peu du genre :

std::vector< cmd* >&
cmd::histo() // fonction membre statique !
{
static std::vector< cmd* >* result
= new std::vector< cmd* > ;
return *result ;
}

(N'oublie pas le lock si tu es en multi-thread.)

Et là, ça ne marche pas, size( ) revoie tjours 0, alors qu'une
trace dans le constructeur de la classe de base m'indique que
size( ) s'incrémente bien à chaque création d'une commande.

Un avis sur la question ?


Je parie que toutes tes commandes sont des objets statiques,
définis dans des sources autres que la source qui contient la
définition de cmd::histo (ou avant cette définition dans la même
source).

--
James Kanze GABI Software


Exact.

Ces objets sont statiques car je trouvais
la représentation :

class Parametres{
public:
Parametres();
~Parametres();

DerivedCommand1 cmd1 ;
DerivedCommand2 cmd2 ;
DerivedCommand3 cmd3 ;

};

assez facile à utiliser :
...
Parametres params;
int value1 ;
string value2 ;

value1 = (int) params.cmd1;
value2 = (string) params.cmd2;
...

Sachant que les operators ( ) sont
définis dans les classes DerivedCommand.

Merci pour les suggestions.
En fait, cette aproche "d'historique" n'est pas primordiale,
c'etait juste une option que je vais laisser de côter...

--
-Stan


Avatar
kanze
Stan (remove 1,2,3) wrote:
a écrit dans le message de
news:

Conceptuellement, cette approche vous semble-t-elle bonne ?


En gros. Je l'utilise souvent. Deux problèmes cependant :

D'abord, tu parles d'une « historique ». Est-ce une
historique, où simplement une liste des objets qui existe à
un moment donné ? Parce que si c'est une historique, tu ne
peux pas stocker des pointeurs à des objets si l'objet sera
détruit sans


Même si c'est le destructeur du dit objet qui s'en charge ?


Qui se charge de quoi ? Tu ne peux pas stocker des pointeurs à
des objects qui ont été détruits ; c'est interdit, et ça donne
un comportement indéfini. Maintenant, SI le destructeur enlève
l'objet de la collection, il n'y a pas de problème, pas parce
que ça te permet de stocker des pointeurs à des objets detruits,
mais parce que ça fait que tu n'en stockes pas.

C'est la solution classique des régistres, encore qu'il faut
faire gaffe dans un environemment multi-thread.

que le pointeur soit enlevé de la collection. Et si on
enlève les pointeurs, ce n'est plus une historique, mais
simplement un régistre.

En fait, c'est juste pour savoir ce que tu essaies à faire.


Effectivement, c'est plus un registre qu'un historique.


D'accord. C'est peut-être une lacune dans mon français, mais
pour moi, le mot « historique » suggère ce que j'appellerais un
« audit trail » en anglais, c-à-d un enrégistrement de tout ce
qui s'est passé. (L'expression anglaise implique une finalité
d'apporter des preuves en cas de litige que je n'entends pas
dans le mot français, mais au niveau du programme, ce qu'on
fait, c'est pareil.)

[...]

La solution classique ici, c'est d'emballer l'objet avec
l'histo dans un singleton, une fonction statique qui en
renvoie une référence à l'objet, en se chargeant de le
construire au premier appel, un peu du genre :

std::vector< cmd* >&
cmd::histo() // fonction membre statique !
{
static std::vector< cmd* >* result
= new std::vector< cmd* > ;
return *result ;
}

(N'oublie pas le lock si tu es en multi-thread.)

Et là, ça ne marche pas, size( ) revoie tjours 0, alors
qu'une trace dans le constructeur de la classe de base
m'indique que size( ) s'incrémente bien à chaque création
d'une commande.

Un avis sur la question ?


Je parie que toutes tes commandes sont des objets statiques,
définis dans des sources autres que la source qui contient
la définition de cmd::histo (ou avant cette définition dans
la même source).


Exact.

Ces objets sont statiques car je trouvais la représentation :

class Parametres{
public:
Parametres();
~Parametres();

DerivedCommand1 cmd1 ;
DerivedCommand2 cmd2 ;
DerivedCommand3 cmd3 ;

};

assez facile à utiliser :
...
Parametres params;
int value1 ;
string value2 ;

value1 = (int) params.cmd1;
value2 = (string) params.cmd2;
...

Sachant que les operators ( ) sont
définis dans les classes DerivedCommand.

Merci pour les suggestions.

En fait, cette aproche "d'historique" n'est pas primordiale,
c'etait juste une option que je vais laisser de côter...


Mais ce n'est pas difficile à implémenter ; il suffit d'utiliser
un singleton pour le map. Je le fais tout le temps. Je te dirais
de régarder la gestion des Option dans CommandLine à ma site,
sauf que 1) je doute que la site marche encore, et 2) j'ai
implémenté des critères de récherche assez complexe, ce qui
cache en grande partie la simplicité de base de ce que j'ai
fait. (Pour le premier, j'ai changé de fournisseur il y a neuf
mois, et je n'ai pas encore tout installé chez le nouveau --
j'ai fait l'erreur idiote de changer la machine, la version de
l'OS et le type de connection tous à la fois, et il me faut un
certain temps pour que tout rémarche correctement.)

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



Avatar
Stan
a écrit dans le message de
news:
Même si c'est le destructeur du dit objet qui s'en charge ?


Effectivement, c'est plus un registre qu'un historique.

D'accord. C'est peut-être une lacune dans mon français, mais
pour moi, le mot « historique » suggère ce que j'appellerais un
« audit trail » en anglais, c-à-d un enrégistrement de tout ce
qui s'est passé. (L'expression anglaise implique une finalité
d'apporter des preuves en cas de litige que je n'entends pas
dans le mot français, mais au niveau du programme, ce qu'on
fait, c'est pareil.)


C'est dû à mon français 'laxiste', fonctionnellement,
ce n'est pas un historique à proprement parler mais
ça me permettait d'avoir une trace *historique* à un instant t.

[...]
En fait, cette aproche "d'historique" n'est pas primordiale,
c'etait juste une option que je vais laisser de côter...

Mais ce n'est pas difficile à implémenter ; il suffit d'utiliser
un singleton pour le map. Je le fais tout le temps. Je te dirais
de régarder la gestion des Option dans CommandLine à ma site,
sauf que 1) je doute que la site marche encore, et 2) j'ai
implémenté des critères de récherche assez complexe, ce qui
cache en grande partie la simplicité de base de ce que j'ai
fait. (Pour le premier, j'ai changé de fournisseur il y a neuf
mois, et je n'ai pas encore tout installé chez le nouveau --
j'ai fait l'erreur idiote de changer la machine, la version de
l'OS et le type de connection tous à la fois, et il me faut un
certain temps pour que tout rémarche correctement.)


Il faut avancer pas à pas, ça évite de trébucher ;-)
Je jetterai un oeil dessus...

--
- Stan