pb: polymorphisme, signature, comparaison d'objets
Le
Isidor.Ducasse
Bonjour,
Je souhaite comparer des objets de classes différentes (mais
appartenant tous à une hiérarchie commune) en parcourant un conteneur
qui voit les objets comme des pointeurs sur la base.
-- Si 2 objets à comparer sont de la même classe, alors la comparaison
doit prendre en compte les attributs spécifiques pour affiner la
comparaison.
-- La classe de base et le conteneur ne doivent pas avoir connaissance
des classes dérivées.
-- les classes dérivées doivent restées indépendantes entre elles.
Je voudrais utiliser un appel par virtualité pour toucher la méthode
C::Compare(const C& ref) définie dans chaque classe, mais comme les
signatures diffèrent, il n'y a pas de surcharge et donc ça appelle
toujours la méthode au niveau de la base. Si je surcharge correctement
(cf. classe D2 du code ci-dessous), je n'ai alors plus accès aux
données spécifiques pour l'objet passé en argument.
Quelqu'un aurait-il une solution élégante?
J'imagine ce problème classique: tri d'un std::list<T*> avec codage
d'un foncteur de comparaison, mais je bute dans mes recherches.
ci-dessous un bout de code qui présente le problème:
//<code>
#include <cstdio>
#include <cstdlib>
class Base
{
int m_Base;
public:
Base()
: m_Base(0)
{
}
virtual void Compare(const Base & ref) const
{
printf("Base m_Base %d %d", m_Base, ref.m_Base);
}
};
class D1 : public Base
{
int m_D1;
public:
D1()
: Base(),
m_D1(1)
{
}
virtual void Compare(const D1 & ref) const
{
Base::Compare(ref);
printf("D1 m_D1 %d %d", m_D1, ref.m_D1);
}
};
class D2 : public Base
{
int m_D2;
public:
D2()
: Base(),
m_D2(2)
{
}
virtual void Compare(const D2 & ref) const
{
Base::Compare(ref);
printf("D2 m_D2 %d %d", m_D2, ref.m_D2);
}
virtual void Compare(const Base & ref) const
{
// ref etant de Base, je ne peux pas utilise ref.m_D2
printf("D2 surcharge Base::Compare ref.m_D2 non utilisable
");
}
};
int main(void)
{
Base base_1, base_2;
D1 d1_1, d1_2;
D2 d2_1, d2_2;
Base* a_1[] = { &base_1, &d1_1, &d2_1 };
Base* a_2[] = { &base_2, &d1_2, &d2_2 };
int nbElt_1= sizeof(a_1)/sizeof(Base*);
int nbElt_2= sizeof(a_2)/sizeof(Base*);
for (int iE=0; iE<nbElt_1; iE++)
{
for (int yE=0; yE<nbElt_2; yE++)
{
printf("%d,%d:", iE, yE);
a_1[iE]->Compare( (*a_2[yE]) );
}
}
return EXIT_SUCCESS;
}
// resultat
0,0:Base m_Base 0 0 // Base vs Base
0,1:Base m_Base 0 0 // Base vs D1
0,2:Base m_Base 0 0 // Base vs D2
1,0:Base m_Base 0 0 // D1 vs Base
1,1:Base m_Base 0 0 // D1 vs DI NOK, je voudrais un
appel à D1::Compare
1,2:Base m_Base 0 0 // D1 vs D2 OK
D2 surcharge Base::Compare ref.m_D2 non utilisable // D2 vs Base
D2 surcharge Base::Compare ref.m_D2 non utilisable // D2 vs D1
D2 surcharge Base::Compare ref.m_D2 non utilisable // D2 vs D2
//</code>
Je souhaite comparer des objets de classes différentes (mais
appartenant tous à une hiérarchie commune) en parcourant un conteneur
qui voit les objets comme des pointeurs sur la base.
-- Si 2 objets à comparer sont de la même classe, alors la comparaison
doit prendre en compte les attributs spécifiques pour affiner la
comparaison.
-- La classe de base et le conteneur ne doivent pas avoir connaissance
des classes dérivées.
-- les classes dérivées doivent restées indépendantes entre elles.
Je voudrais utiliser un appel par virtualité pour toucher la méthode
C::Compare(const C& ref) définie dans chaque classe, mais comme les
signatures diffèrent, il n'y a pas de surcharge et donc ça appelle
toujours la méthode au niveau de la base. Si je surcharge correctement
(cf. classe D2 du code ci-dessous), je n'ai alors plus accès aux
données spécifiques pour l'objet passé en argument.
Quelqu'un aurait-il une solution élégante?
J'imagine ce problème classique: tri d'un std::list<T*> avec codage
d'un foncteur de comparaison, mais je bute dans mes recherches.
ci-dessous un bout de code qui présente le problème:
//<code>
#include <cstdio>
#include <cstdlib>
class Base
{
int m_Base;
public:
Base()
: m_Base(0)
{
}
virtual void Compare(const Base & ref) const
{
printf("Base m_Base %d %d", m_Base, ref.m_Base);
}
};
class D1 : public Base
{
int m_D1;
public:
D1()
: Base(),
m_D1(1)
{
}
virtual void Compare(const D1 & ref) const
{
Base::Compare(ref);
printf("D1 m_D1 %d %d", m_D1, ref.m_D1);
}
};
class D2 : public Base
{
int m_D2;
public:
D2()
: Base(),
m_D2(2)
{
}
virtual void Compare(const D2 & ref) const
{
Base::Compare(ref);
printf("D2 m_D2 %d %d", m_D2, ref.m_D2);
}
virtual void Compare(const Base & ref) const
{
// ref etant de Base, je ne peux pas utilise ref.m_D2
printf("D2 surcharge Base::Compare ref.m_D2 non utilisable
");
}
};
int main(void)
{
Base base_1, base_2;
D1 d1_1, d1_2;
D2 d2_1, d2_2;
Base* a_1[] = { &base_1, &d1_1, &d2_1 };
Base* a_2[] = { &base_2, &d1_2, &d2_2 };
int nbElt_1= sizeof(a_1)/sizeof(Base*);
int nbElt_2= sizeof(a_2)/sizeof(Base*);
for (int iE=0; iE<nbElt_1; iE++)
{
for (int yE=0; yE<nbElt_2; yE++)
{
printf("%d,%d:", iE, yE);
a_1[iE]->Compare( (*a_2[yE]) );
}
}
return EXIT_SUCCESS;
}
// resultat
0,0:Base m_Base 0 0 // Base vs Base
0,1:Base m_Base 0 0 // Base vs D1
0,2:Base m_Base 0 0 // Base vs D2
1,0:Base m_Base 0 0 // D1 vs Base
1,1:Base m_Base 0 0 // D1 vs DI NOK, je voudrais un
appel à D1::Compare
1,2:Base m_Base 0 0 // D1 vs D2 OK
D2 surcharge Base::Compare ref.m_D2 non utilisable // D2 vs Base
D2 surcharge Base::Compare ref.m_D2 non utilisable // D2 vs D1
D2 surcharge Base::Compare ref.m_D2 non utilisable // D2 vs D2
//</code>

Poser une question


Ben, sans un dynamic_cast, je ne vois pas comment faire.
Marc Boyer
--
Si tu peux supporter d'entendre tes paroles
Travesties par des gueux pour exciter des sots
IF -- Rudyard Kipling (Trad. André Maurois)
virtual int compare(Base &b);
ensuite dans le classe Fille : public Base {
virtual int compare(Base &b){
Fille *f=<dynamic_cast>(Fille *)&b;
if(f!=null){
// continuer la comparaison
} else return -1; // différent
}
dans la classe fille je n'ai utilisé que la définition de fille, pas les
autres.
Je ne connais pas cette ecriture. Ca ne serait pas plutot ca:
Fille* f = dynamic_cast< Fille* >( &b );
vous remercie.
J'y ai aussi pensé, et implémenter cette solution faute de mieux (car
ça ne me branche pas d'avoir une telle méthode avec un dynamic_cast
dans chaque classe dérivée, sachant que d'autres développeurs seront
peut-être amener à ajouter des classes dérivés et devront piger
l'intention.
J'ai une autre idée mais que je n'arrive pas à concrétiser: l'idée
consiste à inverser le sens d'appel A.compare(B) en B.compare(A) en
étant dans une méthode de la classe Fille: cela ressemblerais un peu à
ça
class Base
{
virtual int compare(Base &b) = 0;
virtual int compare(Base &b, bool /* arg bidon */) =0;
};
class Fille : public Base
{
virtual int compare(Base &b)
{ return b.compare(*this, true) ; } // *this est de type
Fille
virtual int compare(Base &b, bool /* arg bidon */)
{ return -1; }
virtual int compare(Fille &f, bool /* arg bidon */)
{ return /* ... code! ... */ }
};
On 22 nov, 14:19, Michel Decima
On dirait un 'Double Dispatch'. Mais dans mon souvenir, il faudrait
que toutes les classes filles soient connues de la classe de base.
Mais il y a peut etre des méthodes pour s'affranchir de cette contrainte
(il y a tout un chapitre la dessus dans "Modern C++ Design" d'Alexandrescu).
Ca devrait donner qqch comme ca:
#include <iostream>
class Base;
class DerivA;
class DerivB;
class Base {
public:
int compare( Base const& other ) const
{
return other.compareImpl( *this );
}
virtual int compareImpl( Base const& other ) const = 0;
virtual int compareWithA( DerivA const& ) const = 0;
virtual int compareWithB( DerivB const& ) const = 0;
};
class DerivA : public Base {
public:
virtual int compareImpl( Base const& other ) const
{ return other.compareWithA( *this ); }
virtual int compareWithA( DerivA const& ) const
{ std::cout << "compare A - A" << std::endl; return 0; }
virtual int compareWithB( DerivB const& ) const
{ std::cout << "compare A - B" << std::endl; return 0; }
};
class DerivB : public Base {
public:
virtual int compareImpl( Base const& other ) const
{ return other.compareWithB( *this ); }
virtual int compareWithA( DerivA const& ) const
{ std::cout << "compare B - A" << std::endl; return 0; }
virtual int compareWithB( DerivB const& ) const
{ std::cout << "compare B - B" << std::endl; return 0; }
};
int main() {
DerivA a;
DerivB b;
a.compare( b );
b.compare( a );
a.compare( a );
b.compare( b );
}