Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Aide pour realiser une classe de manipualtion de donnees

45 réponses
Avatar
Aurélien REGAT-BARREL
Bonjour,
Ca parrait long, mais c'est vite lu :-)

J'ai un certain nombre de classes ABC, DEF, GHI, ... qui ont la
particularité d'avoir une certain nombre (presque tout le temps 3) de
"valeurs" toutes de type double. En fait, la seule chose qui différencies
ces classes est le nom de ces valeurs :

class ABC
{
public:
double A() const { return this->a; }
double B() const { return this->b; }
double C() const { return this->c; }
private:
double a;
double b;
double c;
};

class DEF
{
public:
double D() const { return this->d; }
double E() const { return this->e; }
double F() const { return this->f; }
private:
double d;
double e;
double f;
};

etc...
C'est utile de les différencier, car ces valeurs n'ont pas la même
signification, et le typage est nécessaire.
Jusque là tout va bien, tout est calculé correctement.

Probleme :
Je veux dessiner ces valeurs dans un graphe, 1 valeur au choix sur chaque
axe.
Par exemple, dessiner A() et B() sur mon graphe.
Je voudrais donc faire un truc générique, et pas XX fonctions pour chaque
type.
J'ai voulu faire hériter toutes ces classes d'une même classe de base :
Triplet.

class Triplet
{
public:
virtual double Value( int Index ) const = 0;
};

Ainsi je file mon ABC ou DEF ou XYZ à mon graphe et je lui dit "dessine moi
la valeur 1 et 2 et il fait Value( 1 ) et Value ( 2 ) pour les récupérer.

class ABC : public Triplet
{
// idem que plus haut

public:
double Value( int Index )
{
switch ( Index )
{
case 0 : return this->A();
case 1 : return this->B();
case 2 : return this->C();
}
// exception
}
};

Mais Triplet est abstraite, et je ne peux pas contruire un
std::vector<Triplet> à passer pour dessiner.
Je compte du coup re-écrire toutes les classes dans ce style :

class Triplet
{
public:
virtual double Value( int Index ) const { return this->values.at(
Index ); }
private:
std::vector<double> values;
};

class ABC : public Triplet
{
public:
double A() const { return this->values[ 0 ]; }
double B() const { return this->values[ 1 ]; }
double C() const { return this->values[ 2 ]; }
};

Y'a-t-il une meilleure solution ?
Par exemple :

class ABC : public std::vector<double>
{
public:
double A() const { return this->at( 0 ); }
double B() const { return this->at( 1 ); }
double C() const { return this->at( 2 ); }
};

Merci de m'avoir lu.

--
Aurélien REGAT-BARREL

10 réponses

1 2 3 4 5
Avatar
Nicolas Repiquet
"Aurélien REGAT-BARREL" wrote in message
news:40d039f6$0$32165$
Bonjour,


Bonjour.

Mais Triplet est abstraite, et je ne peux pas contruire un
std::vector<Triplet> à passer pour dessiner.
Je compte du coup re-écrire toutes les classes dans ce style :

class Triplet
{
public:
virtual double Value( int Index ) const { return this->values.at(
Index ); }
private:
std::vector<double> values;
};


Pourquoi ne pas garder ta première solution et utiliser un vector<Triplet*>
au lieu de vector<Triplet> ?

-- Nicolas Repiquet

Avatar
Loïc Joly
Aurélien REGAT-BARREL wrote:

Bonjour,
Ca parrait long, mais c'est vite lu :-)

J'ai un certain nombre de classes ABC, DEF, GHI, ... qui ont la
particularité d'avoir une certain nombre (presque tout le temps 3) de
"valeurs" toutes de type double. En fait, la seule chose qui différencies
ces classes est le nom de ces valeurs :


Je propose une autre solution, qui évite de faire de l'héritage :


class ABC
{
public:
double A() const { return this->a; }
double B() const { return this->b; }
double C() const { return this->c; }


Ici, tu ajoutes :
double getVal(int i) const;

private:
double a;
double b;
double c;
};


Et ensuite, tu fait :

template<class Triplet> dessine(Triplet const &t)
{
cout << t.getVal(1);
}

ABC abc;
dessine(abc);

--
Loïc

Avatar
Aurélien Regat-Barrel
Pourquoi ne pas garder ta première solution et utiliser un
vector<Triplet*>

au lieu de vector<Triplet> ?


Merci pour ton aide.
C'est le delete qui m'embête... C'est assez contraignant de devoir appeler
delete sur chaque élément d'un vector reçu en paramètre :
L'idéal serait :

// graphique
void Graph::AddCurve( const std::vector<Triplet> & Values )
{
this->curves.add( Values );
// avec Triplet*, il faudrait parcourir Values et faire des delete...
:-( :-( :-(
}

// autre part
void DrawSelectedTriplets()
{
// liste des triplets à dessiner
std::vector<Triplet> values;

// pour chaque triplet <t>
// si sélectionné
values.push_back( t );
// avec Triplet *, il faudrait allouer un nouveau Triplet copie
de <t> :-( :-( :-(

// dessiner
this->graph->AddCurve( values );
this->graph->DrawCurves();
}

J'ai oublié de préciser que mes Triplets sont souvents temporaires, c.a.d
créés juste avant de les dessiner (dans DrawSelectedTriplets).
Disons que j'ai bien les triplets ABC qui sont persistents, mais on peut
vouloir dessiner CDE, FGH ou XXX qui sont générés à partir de ABC...


--
Aurélien REGAT-BARREL

Avatar
Aurélien Regat-Barrel
Je propose une autre solution, qui évite de faire de l'héritage :


class ABC
{
public:
double A() const { return this->a; }
double B() const { return this->b; }
double C() const { return this->c; }


Ici, tu ajoutes :
double getVal(int i) const;

private:
double a;
double b;
double c;
};


Et ensuite, tu fait :

template<class Triplet> dessine(Triplet const &t)
{
cout << t.getVal(1);
}

ABC abc;
dessine(abc);


Bonjour,
Merci pour ron aide.
Je vois à peu près l'idée, mais :
- je voudrais créer un std::vector de Triplet
- la fonction dessine (ou plutot AddTriplets en vue de les dessiner) est une
fonction membre d'une autre classe "Graph". Comment insérer cette fonction
template dans cette classe qui n'est pas template (et ne peut pas l'être car
dérivant d'une classe d'une lib GUI) ?

--
Aurélien REGAT-BARREL


Avatar
Loïc Joly
Aurélien Regat-Barrel wrote:

Pourquoi ne pas garder ta première solution et utiliser un


vector<Triplet*>

au lieu de vector<Triplet> ?



Merci pour ton aide.
C'est le delete qui m'embête... C'est assez contraignant de devoir appeler
delete sur chaque élément d'un vector reçu en paramètre :


Dans ce cas, soit ton vecteur ne possède pas les triplets, et pas besoin
de faire les delete, soit il les possède (soit de façon partagée ou
suite à une copie en profondeur), et un vecteur de pointeurs
intelligents (par exemple vector<boost::shared_ptr<Triplet> > pour de la
propriété partagée, à condition qu'il y ait des
boost::shared_ptr<Triplet> partout dans ton code) peut convenir.

--
Loïc


Avatar
Aurélien REGAT-BARREL
Dans ce cas, soit ton vecteur ne possède pas les triplets, et pas besoin
de faire les delete, soit il les possède (soit de façon partagée ou
suite à une copie en profondeur), et un vecteur de pointeurs
intelligents (par exemple vector<boost::shared_ptr<Triplet> > pour de la
propriété partagée, à condition qu'il y ait des
boost::shared_ptr<Triplet> partout dans ton code) peut convenir.


J'y ai pensé (à std::auto_ptr).
Mais les pointeurs pour ça ça me gêne beaucoup.
Je viens de penser à aure chose. J'aime bien cette idée d'interface via la
classe abstraite. Probleme : on ne peut pas faire de std::vector de classes
abstraites. Alternative :

class Triplet
{
public:
virtual std::vector<double> GetArray() const = 0;
}:

class ABC : public Triplet
{
// ...
public:
std::vector<double> GetArray() const
{
std::vector<double> array;
array.push_back( this->A() );
array.push_back( this->B() );
array.push_back( this->C() );
return array;
}
};

le code de dessin devient :

void Graph::AddCurve( const std::vector<double> & Values );

void DrawSelectedTriplets()
{
// liste des triplets à dessiner
std::vector<double> values; // nouveauté double à la place de
Triplet

// pour chaque triplet <t>
// si sélectionné
values.push_back( t.GetArray() ); // nouveauté :
t.GetArray()

// dessiner
this->graph->AddCurve( values );
this->graph->DrawCurves();
}

Qu'en pensez-vous ?
Ca me parrait meilleur que ma dernière idée de classe de base Triplet qui
embarque un std::vector que ses dérivées (ABC, ...) utilisent pour stocker
leur A, B et C...

--
Aurélien REGAT-BARREL

Avatar
Loïc Joly
Aurélien REGAT-BARREL wrote:
Dans ce cas, soit ton vecteur ne possède pas les triplets, et pas besoin
de faire les delete, soit il les possède (soit de façon partagée ou
suite à une copie en profondeur), et un vecteur de pointeurs
intelligents (par exemple vector<boost::shared_ptr<Triplet> > pour de la
propriété partagée, à condition qu'il y ait des
boost::shared_ptr<Triplet> partout dans ton code) peut convenir.



J'y ai pensé (à std::auto_ptr).


Tu ne peux pas faire de vector<auto_ptr<T> >, de plus la sémentique
d'auto_ptr est tellement étrange que je déconseille son utilisation.


Mais les pointeurs pour ça ça me gêne beaucoup.


Si tu n'expliques pas en quoi ça te gêne, on va a voir du mal à trouver
une solution qui te plaise.

--
Loïc


Avatar
Aurélien REGAT-BARREL
Mais les pointeurs pour ça ça me gêne beaucoup.


Si tu n'expliques pas en quoi ça te gêne, on va a voir du mal à trouver
une solution qui te plaise.


2 points :
- J'essaye d'utiliser au minimum les pointeurs. Il n'y en pas pratiquement
pas dans tout mon code. Faut allouer, désallouer, vérifier qu'il est pas
null, checker l'allocation,... Jusque là je me suis très bien débrouillé
sans. Mais j'ai peut être atteind les limites du "non pointeur" et il serait
peut être plus élégant d'utiliser des interfaces (ABC) via des pointeurs
intelligents que d'utiliser des classes de base faussement abstraites via un
constrcuteur protected.
- Utiliser des pointeurs (intelligents ou non) va me faire modifier pas mal
de code.

Mais je garde l'idée sous la main, au moins pour le prochain problème du
genre.
Merci.

--
Aurélien REGAT-BARREL


Avatar
kanze
"Aurélien REGAT-BARREL" wrote in
message news:<40d039f6$0$32165$...

Ca parrait long, mais c'est vite lu :-)

J'ai un certain nombre de classes ABC, DEF, GHI, ... qui ont la
particularité d'avoir une certain nombre (presque tout le temps 3) de
"valeurs" toutes de type double. En fait, la seule chose qui
différencies ces classes est le nom de ces valeurs :

class ABC
{
public:
double A() const { return this->a; }
double B() const { return this->b; }
double C() const { return this->c; }
private:
double a;
double b;
double c;
};

class DEF
{
public:
double D() const { return this->d; }
double E() const { return this->e; }
double F() const { return this->f; }
private:
double d;
double e;
double f;
};

etc...

C'est utile de les différencier, car ces valeurs n'ont pas la même
signification, et le typage est nécessaire. Jusque là tout va bien,
tout est calculé correctement.

Probleme :

Je veux dessiner ces valeurs dans un graphe, 1 valeur au choix sur
chaque axe. Par exemple, dessiner A() et B() sur mon graphe. Je
voudrais donc faire un truc générique, et pas XX fonctions pour chaque
type. J'ai voulu faire hériter toutes ces classes d'une même classe de
base : Triplet.

class Triplet
{
public:
virtual double Value( int Index ) const = 0;
};

Ainsi je file mon ABC ou DEF ou XYZ à mon graphe et je lui dit
"dessine moi la valeur 1 et 2 et il fait Value( 1 ) et Value ( 2 )
pour les récupérer.

class ABC : public Triplet
{
// idem que plus haut

public:
double Value( int Index )
{
switch ( Index )
{
case 0 : return this->A();
case 1 : return this->B();
case 2 : return this->C();
}
// exception
}
};

Mais Triplet est abstraite, et je ne peux pas contruire un
std::vector<Triplet> à passer pour dessiner.
Je compte du coup re-écrire toutes les classes dans ce style :

class Triplet
{
public:
virtual double Value( int Index ) const { return this->values.at(
Index ); }
private:
std::vector<double> values;
};

class ABC : public Triplet
{
public:
double A() const { return this->values[ 0 ]; }
double B() const { return this->values[ 1 ]; }
double C() const { return this->values[ 2 ]; }
};

Y'a-t-il une meilleure solution ?


Ça ne me semble pas mauvais.

Ceci dit, j'ai lu la reste de la discussion, où tu dis de ne pas aimer
les pointeurs. Or, en C++, qu'on les aime ou non, le polymorphisme
implique l'utilisation des pointeurs. Et un certain nombre d'autres
choses. Si tes classes ABC et DEF sont réelement des valeurs, avec une
sémantique de valeur, c'est peut-être que le polymorphisme, même limité,
ne leur convient pas.

En plus de la solution template suggérer par Loïc, tu pourrais penser à
une conversion implicite -- tu définis une class Triplet uniquement pour
l'affichage, puis dans ABC, DEF, etc., tu définis un opérateur de
conversion :

operator Triplet() {
return ...
}

où tu construis un Triplet des valeurs dans la classe dérivée.

--
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
Aurélien REGAT-BARREL
Ça ne me semble pas mauvais.

Ceci dit, j'ai lu la reste de la discussion, où tu dis de ne pas aimer
les pointeurs. Or, en C++, qu'on les aime ou non, le polymorphisme
implique l'utilisation des pointeurs. Et un certain nombre d'autres
choses.


Peux-tu détailler un petit peu. Pourquoi cela implique-t-il les pointeurs ?

Si tes classes ABC et DEF sont réelement des valeurs, avec une
sémantique de valeur, c'est peut-être que le polymorphisme, même limité,
ne leur convient pas.


Eh bien, si l'on considère que le polymorphisme est à utiliser lors d'une
relation "est un", je pense qu'il est adapté ici.
ABC, DEF, ... sont tous des Triplet. La seule chose qui les différencie,
c'est le contexte de leurs valeurs.
ABC va être les coordonnées d'un point en mètres, DEF en yards, GHI en
kilomètres, etc...
Il est donc important de les différencier pour les manipuler.
Mais pour les afficher, j'ai un graphe qui se calibre automatiquement en
fonction du min et du max.
La fonction qui lui donne les Triplets à afficher se charge de mettre la
légende à jour. En fait, mon graphe est un bête graphe générique qui ne fait
que dessiner des Triplet. Passer par Triplet me permet de ne pas devoir le
spécialiser pour chaque nouveau type de Triplet, réduction du couplage, ...

En plus de la solution template suggérer par Loïc, tu pourrais penser à
une conversion implicite -- tu définis une class Triplet uniquement pour
l'affichage, puis dans ABC, DEF, etc., tu définis un opérateur de
conversion :

operator Triplet() {
return ...
}

où tu construis un Triplet des valeurs dans la classe dérivée.


Intéressant... Je garde ça sous le coude.
Merci pour ta réponse.

--
Aurélien REGAT-BARREL

1 2 3 4 5