GNT sans publicité, site mobile, fonctionnalitées exclusives...

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>
Lire les 15 réponses

Vidéos High-Tech et Jeu Vidéo
Téléchargements
Vos réponses Page 1 / 3
Gagnez chaque mois un abonnement Premium avec GNT : Inscrivez-vous !
Trier par : date / pertinence
Marc Boyer
Le #312672
On 2007-11-22,
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.


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)

Aris
Le #312670
On 2007-11-22,
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.


Ben, sans un dynamic_cast, je ne vois pas comment faire.

Marc Boyer
bon dans la classe de base, tu as une methode

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.


Michel Decima
Le #312669

Fille *f=<dynamic_cast>(Fille *)&b;


Je ne connais pas cette ecriture. Ca ne serait pas plutot ca:

Fille* f = dynamic_cast< Fille* >( &b );

Isidor.Ducasse
Le #312668
Fille* f = dynamic_cast< Fille* >( &b );
est en effet la bonne écriture. Le principe est bon et fonctionne, je

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

Fille *f=<dynamic_cast>(Fille *)&b;


Je ne connais pas cette ecriture. Ca ne serait pas plutot ca:

Fille* f = dynamic_cast< Fille* >( &b );



Michel Decima
Le #312714
Fille* f = dynamic_cast< Fille* >( &b );
est en effet la bonne écriture. Le principe est bon et fonctionne, je

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.


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 );
}


Publicité
Suivre les réponses
Poster une réponse
Anonyme