Bonjour,
J'aimerais bien créer quelques classes qui dérivent de certains conteneurs
de la STL. Je sais que ces derniers ne sont pas conçus pour être dérivés,
mais sachant que le but est simplement de :
- créer un vrai nouveau type au lieu d'un typedef
- ne pas ajouter de donnée membre mais uniquement dfes fonctions
est-ce une pratique acceptable ?
Exemple:
class SortedData : public std::set<int>
{
public:
SortedData( const std::vector<int> & Vect )
{
this->insert( Vect.begin(), Vect.end() );
}
// calcule la moyenne
double Average() const
{
int total = 0;
total = std::accumulate( this->begin(), this->end(), total );
return total * 1.0 / this->size();
}
};
| Averager& operator+=( T a ) | { | v += a ; | ++ n ; | return *this ; | }
| operator T() const | { | return v / n ; | } | private: | T v ; | size_t n ; | } ;
| template< typename T > | Averager< T > | operator+( Averager< T > const& l, Averager< T > const& r ) | { | Averager< T > result( l ) ; | result += r ; | return result ; | }
(Au fond, je trouve accumulate bien sympa ; pour une fois, même le nom convient. Je m'en sers donc pour le calcul de CRC ou de MD5 -- mes classes ne fournissent que des accumulateurs spéciaux.)
-- James Kanze GABI Software http://www.gabi-soft.fr 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
Falk Tannhäuser wrote:
Aurélien REGAT-BARREL wrote:
class SortedData : public std::set<int>
{
public:
SortedData( const std::vector<int> & Vect )
{
this->insert( Vect.begin(), Vect.end() );
}
// calcule la moyenne
double Average() const
{
int total = 0;
total = std::accumulate( this->begin(), this->end(), total
);
return total * 1.0 / this->size();
}
};
Pourquoi Average() doit-elle absolument être une fonction
membre ? Quelque chose comme
| Averager& operator+=( T a )
| {
| v += a ;
| ++ n ;
| return *this ;
| }
| operator T() const
| {
| return v / n ;
| }
| private:
| T v ;
| size_t n ;
| } ;
| template< typename T >
| Averager< T >
| operator+( Averager< T > const& l, Averager< T > const& r )
| {
| Averager< T > result( l ) ;
| result += r ;
| return result ;
| }
(Au fond, je trouve accumulate bien sympa ; pour une fois, même
le nom convient. Je m'en sers donc pour le calcul de CRC ou de
MD5 -- mes classes ne fournissent que des accumulateurs
spéciaux.)
--
James Kanze GABI Software http://www.gabi-soft.fr
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
| Averager& operator+=( T a ) | { | v += a ; | ++ n ; | return *this ; | }
| operator T() const | { | return v / n ; | } | private: | T v ; | size_t n ; | } ;
| template< typename T > | Averager< T > | operator+( Averager< T > const& l, Averager< T > const& r ) | { | Averager< T > result( l ) ; | result += r ; | return result ; | }
(Au fond, je trouve accumulate bien sympa ; pour une fois, même le nom convient. Je m'en sers donc pour le calcul de CRC ou de MD5 -- mes classes ne fournissent que des accumulateurs spéciaux.)
-- James Kanze GABI Software http://www.gabi-soft.fr 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
Falk Tannhäuser
Jean-Marc Bourguet wrote:
- il peut y avoir un decalage entre le pointeur vers la classe de base et celui de la classe derivee. A nouveau douteux dans des cas simples, mais si on utilise de l'heritage multiple, c'est certain que ce sera le cas pour au moins une des bases.
Ça peut aussi arriver dans le cas d'héritage virtuel, ou encore si une classe polymorphique dérive d'une classe non polymorphique (c.a.d. si au moins une fonction virtuelle est ajoutée dans la classe dérivée, les containers standard n'en comportant pas).
Falk
Jean-Marc Bourguet wrote:
- il peut y avoir un decalage entre le pointeur vers la classe de
base et celui de la classe derivee. A nouveau douteux dans des
cas simples, mais si on utilise de l'heritage multiple, c'est
certain que ce sera le cas pour au moins une des bases.
Ça peut aussi arriver dans le cas d'héritage virtuel, ou encore
si une classe polymorphique dérive d'une classe non polymorphique
(c.a.d. si au moins une fonction virtuelle est ajoutée dans la
classe dérivée, les containers standard n'en comportant pas).
- il peut y avoir un decalage entre le pointeur vers la classe de base et celui de la classe derivee. A nouveau douteux dans des cas simples, mais si on utilise de l'heritage multiple, c'est certain que ce sera le cas pour au moins une des bases.
Ça peut aussi arriver dans le cas d'héritage virtuel, ou encore si une classe polymorphique dérive d'une classe non polymorphique (c.a.d. si au moins une fonction virtuelle est ajoutée dans la classe dérivée, les containers standard n'en comportant pas).
Falk
Falk Tannhäuser
Aurélien REGAT-BARREL wrote:
Faire un "delete" d'un pointeur de classe de base pointant sur un objet de classe dérivée engendre un comportement indéfini sauf si la classe de base comporte un destructeur virtuel.
Ca je le sais. Ce que je ne comprenais pas c'est comment ça pouvait p oser problème avec une classe & son destructeur vides :
class Base { public: int Data; };
class A : public Base { public: void print() { std::cout << this->Data << 'n'; } };
Base * b = new A; delete b;
Si dans ta classe A tu as redéfini new et delete :
class A : public Base { public: void print() const { ... }
l'opérateur redéfini A::new sera appelé dans l'expression Base* b = new A; mais l'appel de A::delete n'est garanti dans delete b; que si Base a un destructeur virtuel ; sinon c'est opérateur global ::new qui risque d'être appelé et rien ne dit que celui-ci traite correctement les blocs mémoire alloués par my_special_alloc comme le ferait my_special_free (d'où risque de fuite de mémoire, de corruption des données ou de plantage).
Falk
Aurélien REGAT-BARREL wrote:
Faire un "delete" d'un pointeur de classe de base pointant sur un
objet de classe dérivée engendre un comportement indéfini sauf si
la classe de base comporte un destructeur virtuel.
Ca je le sais. Ce que je ne comprenais pas c'est comment ça pouvait p oser
problème avec une classe & son destructeur vides :
class Base
{
public:
int Data;
};
class A : public Base
{
public:
void print() { std::cout << this->Data << 'n'; }
};
Base * b = new A;
delete b;
Si dans ta classe A tu as redéfini new et delete :
class A : public Base
{
public:
void print() const { ... }
l'opérateur redéfini A::new sera appelé dans l'expression
Base* b = new A;
mais l'appel de A::delete n'est garanti dans
delete b;
que si Base a un destructeur virtuel ; sinon c'est opérateur
global ::new qui risque d'être appelé et rien ne dit que
celui-ci traite correctement les blocs mémoire alloués
par my_special_alloc comme le ferait my_special_free
(d'où risque de fuite de mémoire, de corruption des données
ou de plantage).
Faire un "delete" d'un pointeur de classe de base pointant sur un objet de classe dérivée engendre un comportement indéfini sauf si la classe de base comporte un destructeur virtuel.
Ca je le sais. Ce que je ne comprenais pas c'est comment ça pouvait p oser problème avec une classe & son destructeur vides :
class Base { public: int Data; };
class A : public Base { public: void print() { std::cout << this->Data << 'n'; } };
Base * b = new A; delete b;
Si dans ta classe A tu as redéfini new et delete :
class A : public Base { public: void print() const { ... }
l'opérateur redéfini A::new sera appelé dans l'expression Base* b = new A; mais l'appel de A::delete n'est garanti dans delete b; que si Base a un destructeur virtuel ; sinon c'est opérateur global ::new qui risque d'être appelé et rien ne dit que celui-ci traite correctement les blocs mémoire alloués par my_special_alloc comme le ferait my_special_free (d'où risque de fuite de mémoire, de corruption des données ou de plantage).
Falk
noone
Il me semble que rendre le fait que ta representation actuelle est une liste *n'est pas* un bon choix. Par exemples, une modification qui devrait etre transparente est le changement de la structure de donnee pour rendre la recherche de la charge la plus proche ou des charges visibles dans une fenetre plus efficace.
justement il me semble que ma représentation encapsule la liste...
Il me semble que rendre le fait que ta representation actuelle est une
liste *n'est pas* un bon choix. Par exemples, une modification qui
devrait etre transparente est le changement de la structure de donnee
pour rendre la recherche de la charge la plus proche ou des charges
visibles dans une fenetre plus efficace.
justement il me semble que ma représentation encapsule la liste...
Il me semble que rendre le fait que ta representation actuelle est une liste *n'est pas* un bon choix. Par exemples, une modification qui devrait etre transparente est le changement de la structure de donnee pour rendre la recherche de la charge la plus proche ou des charges visibles dans une fenetre plus efficace.
justement il me semble que ma représentation encapsule la liste...
noone
Ces fonctions-là doivent-elles en être membres ? J'aurais vu plutôt des fonctions libres ; éventuellement même des fonctions template (à deux itérateurs).
pourquoi 2 itérateurs ?
(Pour la première, je me démande
même s'il ne faut pas une fonction find_best, puis un object fonctionnel pour comparer la distance -- bind2nd de Distance, peut-être.
Qu'est-ce qu'un objet fonctionnel ? bind2nd ?
Pour la deuxième, il y a déjà find_if,
et pour la
troisième, for_each.
oui
Chaque fois avec le Predicate ou l'objet
fonctionnel qui convient.)
j'avoue ne pas être très à l'aise avec ce concept de "Predicate"
Ces fonctions-là doivent-elles en être membres ? J'aurais vu
plutôt des fonctions libres ; éventuellement même des fonctions
template (à deux itérateurs).
pourquoi 2 itérateurs ?
(Pour la première, je me démande
même s'il ne faut pas une fonction find_best, puis un object
fonctionnel pour comparer la distance -- bind2nd de Distance,
peut-être.
Qu'est-ce qu'un objet fonctionnel ?
bind2nd ?
Pour la deuxième, il y a déjà find_if,
et pour la
troisième, for_each.
oui
Chaque fois avec le Predicate ou l'objet
fonctionnel qui convient.)
j'avoue ne pas être très à l'aise avec ce concept de "Predicate"
Ces fonctions-là doivent-elles en être membres ? J'aurais vu plutôt des fonctions libres ; éventuellement même des fonctions template (à deux itérateurs).
pourquoi 2 itérateurs ?
(Pour la première, je me démande
même s'il ne faut pas une fonction find_best, puis un object fonctionnel pour comparer la distance -- bind2nd de Distance, peut-être.
Qu'est-ce qu'un objet fonctionnel ? bind2nd ?
Pour la deuxième, il y a déjà find_if,
et pour la
troisième, for_each.
oui
Chaque fois avec le Predicate ou l'objet
fonctionnel qui convient.)
j'avoue ne pas être très à l'aise avec ce concept de "Predicate"
Fabien LE LEZ
On Fri, 21 Jan 2005 16:44:25 +0100, "" :
justement il me semble que ma représentation encapsule la liste...
Si elle encapsule correctement la liste, l'utilisateur de la classe ne doit même pas être au courant qu'il y a un membre de type std::list<>.
En fait, dès que tu vois un truc du style
class C { public: int GetX() const { return x; } void SetX (int nouveau_x) { x= nouveau_x; } private: int x; };
demande-toi si casser ainsi l'encapsulation (i.e. demander à l'utilisateur de la classe de connaître la présence du membre privé x) est vraiment une bonne chose. Ça l'est rarement.
-- ;-)
On Fri, 21 Jan 2005 16:44:25 +0100, "noone@nowhere.com"
<noone@nowhere.com>:
justement il me semble que ma représentation encapsule la liste...
Si elle encapsule correctement la liste, l'utilisateur de la classe ne
doit même pas être au courant qu'il y a un membre de type std::list<>.
En fait, dès que tu vois un truc du style
class C
{
public:
int GetX() const { return x; }
void SetX (int nouveau_x) { x= nouveau_x; }
private:
int x;
};
demande-toi si casser ainsi l'encapsulation (i.e. demander à
l'utilisateur de la classe de connaître la présence du membre privé x)
est vraiment une bonne chose. Ça l'est rarement.
justement il me semble que ma représentation encapsule la liste...
Si elle encapsule correctement la liste, l'utilisateur de la classe ne doit même pas être au courant qu'il y a un membre de type std::list<>.
En fait, dès que tu vois un truc du style
class C { public: int GetX() const { return x; } void SetX (int nouveau_x) { x= nouveau_x; } private: int x; };
demande-toi si casser ainsi l'encapsulation (i.e. demander à l'utilisateur de la classe de connaître la présence du membre privé x) est vraiment une bonne chose. Ça l'est rarement.
-- ;-)
noone
Charges ma_liste; ma_liste.Affiche (cout);
En fait le problème n'est pas l'affichage mais le calcul des champs E et B.
Je le fait dans le prog principal car il faut connaitre la grille pour le calcul...
Charges ma_liste;
ma_liste.Affiche (cout);
En fait le problème n'est pas l'affichage mais le calcul des champs E et B.
Je le fait dans le prog principal car il faut connaitre la grille pour
le calcul...
En fait le problème n'est pas l'affichage mais le calcul des champs E et B.
Je le fait dans le prog principal car il faut connaitre la grille pour le calcul...
noone
demande-toi si casser ainsi l'encapsulation (i.e. demander à l'utilisateur de la classe de connaître la présence du membre privé x) est vraiment une bonne chose. Ça l'est rarement.
et je fais comment alors pour interagir avec ma grille pour faire le calcul de E et B ?
demande-toi si casser ainsi l'encapsulation (i.e. demander à
l'utilisateur de la classe de connaître la présence du membre privé x)
est vraiment une bonne chose. Ça l'est rarement.
et je fais comment alors pour interagir avec ma grille pour faire le
calcul de E et B ?
demande-toi si casser ainsi l'encapsulation (i.e. demander à l'utilisateur de la classe de connaître la présence du membre privé x) est vraiment une bonne chose. Ça l'est rarement.
et je fais comment alors pour interagir avec ma grille pour faire le calcul de E et B ?
Jean-Marc Bourguet
"" writes:
Il me semble que rendre le fait que ta representation actuelle est une liste *n'est pas* un bon choix. Par exemples, une modification qui devrait etre transparente est le changement de la structure de donnee pour rendre la recherche de la charge la plus proche ou des charges visibles dans une fenetre plus efficace.
justement il me semble que ma représentation encapsule la liste...
class SortedData : public std::set<Charge>
n'est certainement pas une encapsulation. Meme
class SortedData : private std::set<Charge>
n'en est pas si tu publies que tu fournis des std:set<int>::iterator. En fait j'aurais meme tendance a ne pas me contenter d'un typedef. L'objectif est de pouvoir remplacer le std::set<Charge> par un RTree<Charge> sans probleme.
Je n'heriterais meme pas, et je n'appellerais pas la class SortedData.
A+
-- Jean-Marc FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html Site de usenet-fr: http://www.usenet-fr.news.eu.org
"noone@nowhere.com" <noone@nowhere.com> writes:
Il me semble que rendre le fait que ta representation actuelle est une
liste *n'est pas* un bon choix. Par exemples, une modification qui
devrait etre transparente est le changement de la structure de donnee
pour rendre la recherche de la charge la plus proche ou des charges
visibles dans une fenetre plus efficace.
justement il me semble que ma représentation encapsule la liste...
class SortedData : public std::set<Charge>
n'est certainement pas une encapsulation. Meme
class SortedData : private std::set<Charge>
n'en est pas si tu publies que tu fournis des std:set<int>::iterator.
En fait j'aurais meme tendance a ne pas me contenter d'un typedef.
L'objectif est de pouvoir remplacer le std::set<Charge> par un
RTree<Charge> sans probleme.
Je n'heriterais meme pas, et je n'appellerais pas la class SortedData.
A+
--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org
Il me semble que rendre le fait que ta representation actuelle est une liste *n'est pas* un bon choix. Par exemples, une modification qui devrait etre transparente est le changement de la structure de donnee pour rendre la recherche de la charge la plus proche ou des charges visibles dans une fenetre plus efficace.
justement il me semble que ma représentation encapsule la liste...
class SortedData : public std::set<Charge>
n'est certainement pas une encapsulation. Meme
class SortedData : private std::set<Charge>
n'en est pas si tu publies que tu fournis des std:set<int>::iterator. En fait j'aurais meme tendance a ne pas me contenter d'un typedef. L'objectif est de pouvoir remplacer le std::set<Charge> par un RTree<Charge> sans probleme.
Je n'heriterais meme pas, et je n'appellerais pas la class SortedData.
A+
-- Jean-Marc FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html Site de usenet-fr: http://www.usenet-fr.news.eu.org
Fabien LE LEZ
On Fri, 21 Jan 2005 16:48:20 +0100, "" :
pourquoi 2 itérateurs ?
C'est l'idiome STL : toute fonction qui doit agir sur un ensemble d'éléments prend comme paramètre un itérateur sur le premier élément et un itérateur sur l'élément "past the end" (i.e. l'itérateur qui suit directement le dernier élément).
-- ;-)
On Fri, 21 Jan 2005 16:48:20 +0100, "noone@nowhere.com"
<noone@nowhere.com>:
pourquoi 2 itérateurs ?
C'est l'idiome STL : toute fonction qui doit agir sur un ensemble
d'éléments prend comme paramètre un itérateur sur le premier élément
et un itérateur sur l'élément "past the end" (i.e. l'itérateur qui
suit directement le dernier élément).
C'est l'idiome STL : toute fonction qui doit agir sur un ensemble d'éléments prend comme paramètre un itérateur sur le premier élément et un itérateur sur l'élément "past the end" (i.e. l'itérateur qui suit directement le dernier élément).