OVH Cloud OVH Cloud

Héritage et algorithme

14 réponses
Avatar
Michael
Bonsoir à tous,

soient les classes suivantes:

class ObjetBase
{
ObjetBase() {}
};

class ObjetThumb : public ObjetBase
{
ObjetThumb() : ObjetBase() {}

void DrawThumb() { }
};

class ObjetText : public ObjetBase
{
ObjetText() : ObjetBase() {}

void DrawText() { }
};

class ListeBase
{
protected:
std::vector<ObjetBase*> liste;
};

class ListeThumb : public ListeBase
{
public:
void foo();
};

class ListeText : public ListeBase
{
public:
void foo();
};

////////////////////////////

J'aurais voulu implémenter les fonctions foo comme suit:

void ListeThumb::foo()
{
std::for_each(liste.begin(),liste.end(),std::mem_fun
(&ObjetThumb::DrawThumb));
}

void ListeText::foo()
{
std::for_each(liste.begin(),liste.end(),std::mem_fun
(&ObjetText::DrawText));
}

Mais bien sûr ça ne fonctionne pas, j'ai les messages d'erreur suivants
(BCB6)

[C++ Erreur] _algo.h(65): E2034 Impossible de convertir 'ObjetBase *' en
'const ObjetThumb *'
[C++ Erreur] _algo.h(65): E2342 Mauvaise correspondance de type dans le
paramètre '__x' ('const ObjetThumb * const &' désiré, 'ObjetBase *'
obtenu)

Alors du coup je suis obligé de déclarer DrawThumb et DrawText virtuelles
dans ObjetBase, alors qu'elles ne sont utilisées respectivement que dans
ObjetThumb et ObjetText.

Y a-t-il moyen de faire autrement?

Sinon par rapport au même cas de figure, existe-il un pattern quelconque,
ou une méthode à suivre, pour m'assurer que les ObjetThumb sont bien
utilisés par ListeThumb et ObjetText par ListeText?

Merci d'avance

Mike

10 réponses

1 2
Avatar
Fabien LE LEZ
On 20 Jan 2006 03:41:19 GMT, Michael
:

soient les classes suivantes:


J'ai l'impression que tu te compliques inutilement la vie. Voici ce
que je propose :


class ObjetBase
{
public:


ObjetBase() {}


virtual ~ObjetBase() {}
virtual void Draw()= 0;

};

class ObjetThumb : public ObjetBase
{
ObjetThumb() : ObjetBase() {}

void Draw() { }


};

class ObjetText : public ObjetBase
{
ObjetText() : ObjetBase() {}

void Draw() { }

};

class ListeBase
{
protected:
std::vector<ObjetBase*> liste;
void foo()

{
std::for_each (liste.begin(), liste.end(),
std::mem_fun (&ObjetBase::Draw));
}

[Ici, un destructeur virtuel serait sans doute indiqué également]

};

class ListeThumb : public ListeBase
{
public:
};

class ListeText : public ListeBase
{
public:
};


Avatar
Fabien LE LEZ
On 20 Jan 2006 03:41:19 GMT, Michael
:

existe-il un pattern quelconque,
ou une méthode à suivre, pour m'assurer que les ObjetThumb sont bien
utilisés par ListeThumb et ObjetText par ListeText?


Tu veux dire, que aucun objet de classe ObjetThumb ne puisse être
inséré dans un ListeText ?

Il faut vérifier, à chaque insertion (ou une fois pour toutes à la fin
des insertions), que pour chaque pointeur p inséré,
dynamic_cast<ObjetText*>(p) != 0.

Ne sachant comment tu remplis tes tableaux, je ne saurais t'en dire
plus.

Avatar
Michael
J'ai l'impression que tu te compliques inutilement la vie. Voici ce
que je propose :


J'ai effectivement choisi un exemple pas trop clair, je n'aurais pas du
préfixer les 2 fonctions avec Draw. Et en plus je n'ai pas de fonction
équivalente dans la classe ObjetText. Il était trop tard (tôt?) quand
j'ai posté :(

class ObjetBase
{
ObjetBase() {}
};

class ObjetThumb : public ObjetBase
{
ObjetThumb() : ObjetBase() {}

void DrawPreview() { }
};

class ObjetText : public ObjetBase
{
ObjetText() : ObjetBase() {}
};

class ListeBase
{
protected:
std::vector<ObjetBase*> liste;
};

class ListeThumb : public ListeBase
{
public:
void foo()
{
std::for_each(liste.begin(),liste.end(),std::mem_fun
(&ObjetThumb::DrawPreview));
}
};

Voilà, c'est mieux comme ça. Donc du coup la question se repose:
Y a-t-il une manière de faire qui m'évite de déclarer DrawPreview en
virtual dans ObjetBase?

Avatar
Michael
Tu veux dire, que aucun objet de classe ObjetThumb ne puisse être
inséré dans un ListeText ?

Il faut vérifier, à chaque insertion (ou une fois pour toutes à la fin
des insertions), que pour chaque pointeur p inséré,
dynamic_cast<ObjetText*>(p) != 0.

Ne sachant comment tu remplis tes tableaux, je ne saurais t'en dire
plus.


class ListeObjetsBase
{
private:
std::vector<ObjetBase*> liste;
protected:
void AddObject(ObjetBase * objet)
{
liste.push_back(objet);
}
};

class ListeObjetsThumb : public ListeObjetsBase
{
public:
void CreateObject()
{
ObjetThumb * objet = new ObjetThumb();
AddObject(objet);
//Gestion de la représentation sous forme graphique de l'objet
}
};

class ListeObjetsText : public ListeObjetsBase
{
public:
void CreateObject()
{
ObjetText * objet = new ObjetText();
AddObject(objet);
//Gestion de la représentation sous forme texte de l'objet
}
};

Avatar
Cyrille
J'ai l'impression que tu te compliques inutilement la vie. Voici ce
que je propose :


J'ai effectivement choisi un exemple pas trop clair, je n'aurais pas du
préfixer les 2 fonctions avec Draw. Et en plus je n'ai pas de fonction
équivalente dans la classe ObjetText. Il était trop tard (tôt?) quand
j'ai posté :(

class ObjetBase
{
ObjetBase() {}
};

class ObjetThumb : public ObjetBase
{
ObjetThumb() : ObjetBase() {}

void DrawPreview() { }
};

class ObjetText : public ObjetBase
{
ObjetText() : ObjetBase() {}
};

class ListeBase
{
protected:
std::vector<ObjetBase*> liste;
};

class ListeThumb : public ListeBase
{
public:
void foo()
{
std::for_each(liste.begin(),liste.end(),std::mem_fun
(&ObjetThumb::DrawPreview));
}
};

Voilà, c'est mieux comme ça. Donc du coup la question se repose:
Y a-t-il une manière de faire qui m'évite de déclarer DrawPreview en
virtual dans ObjetBase?


Tu voudrais, si j'ai bien compris, que ton for_each appelle le
DrawPreview de ObjetThumb quand bien même il n'a que des pointeurs sur
ObjetBase à sa disposition.
A première vue, je me demanderais: "mais pourquoi est-ce que ListeThumb
ne contient pas simplement un std::vector<ObjetThumb*>" ? Un ListeThumb
est-il susceptible de contenir autre chose que des ObjetThumb? Si ce
n'est pas le cas, il y a une erreur de conception à vouloir utiliser un
vecteur d'ObjetBase à l'intérieur, à mon sens.

Si vraiment tu veux faire ça, tu peux t'en sortir avec une fonction libre:

void DrawPreviewImpl(ObjetBase * obj)
{
try
{
ObjetThumb * thumb = dynamic_cast<ObjetThumb *>( obj );
thumb->DrawPreview();
}
catch (std::bad_cast)
{
// ne devrait pas arriver là
}
}

et ainsi utiliser for_each ainsi:
std::for_each(liste.begin(), liste.end(), DrawPreviewImpl);

Aussi, pour faire un dynamic_cast sur un pointeur d'ObjetBase, il faut
que ce soit une classe polymorphe, c'est-à-dire qu'elle contient au
moins une fonction virtuelle. Comme de toute façons il faudrait que tu
mettes aussi un destructeur virtuel dans cette classe, celui-ci suffit.

--
Réhabilitez Garfieldd!
http://minilien.com/?HpC5r1qClU


Avatar
Michael
Tu voudrais, si j'ai bien compris, que ton for_each appelle le
DrawPreview de ObjetThumb quand bien même il n'a que des pointeurs sur
ObjetBase à sa disposition.
A première vue, je me demanderais: "mais pourquoi est-ce que
ListeThumb ne contient pas simplement un std::vector<ObjetThumb*>" ?
Un ListeThumb est-il susceptible de contenir autre chose que des
ObjetThumb? Si ce n'est pas le cas, il y a une erreur de conception à
vouloir utiliser un vecteur d'ObjetBase à l'intérieur, à mon sens.


Parce que j'ai aussi une classe ListeText qui s'appuie sur std::vector
<ObjetBase*>.

En fait, ListeBase propose les implémentations concernant la gestion de
cette liste. ListeThumb et ListeText déterminent simplement la manière
dont seront affichés les Objet.

Par exemple, pour la copie d'un objet:

void ListeBase::CopyObject(const int objectId, const int newId)
{
//On récupère l'objet
ObjetBase * object = GetObjectFromIndex(objectId);

//On le copie
ObjetBase * pastedObject = new ObjetBase(object);

//On l'attribue
pastedObject->ChangeIndex(newId);

//On ajoute l'objet
AddExistingObject(pastedObject);

//On réorganise les séquences
SortTimeline();
}

void ListeThumb::CopyObject(const int idObject, const int newId)
{
//Copie de l'objet dans le vecteur
ListeBase::CopyObject(idObject,newId);

//Représentation graphique du copier (Affichage d'une icône sur l'image
de prévisualisation de l'objet par exemple)
//...
}

void ListeText::CopyObject(const int idObject, const int newId)
{
//Copie de l'objet dans le vecteur
ListeBase::CopyObject(idObject,newId);

//Représentation texte du copier
//...
}

Si vraiment tu veux faire ça, tu peux t'en sortir avec une fonction
libre:

void DrawPreviewImpl(ObjetBase * obj)
{
try
{
ObjetThumb * thumb = dynamic_cast<ObjetThumb *>( obj );
thumb->DrawPreview();
}
catch (std::bad_cast)
{
// ne devrait pas arriver là
}
}

et ainsi utiliser for_each ainsi:
std::for_each(liste.begin(), liste.end(), DrawPreviewImpl);

Aussi, pour faire un dynamic_cast sur un pointeur d'ObjetBase, il faut
que ce soit une classe polymorphe, c'est-à-dire qu'elle contient au
moins une fonction virtuelle. Comme de toute façons il faudrait que tu
mettes aussi un destructeur virtuel dans cette classe, celui-ci
suffit.



Avatar
Fabien LE LEZ
On Fri, 20 Jan 2006 13:46:31 +0100, Cyrille :

try
{
ObjetThumb * thumb = dynamic_cast<ObjetThumb *>( obj );


dynamic_cast<> sur un pointeur ne lance pas d'exception. Il renvoie
éventuellement un pointeur nul.

Avatar
Fabien LE LEZ
On 20 Jan 2006 12:34:59 GMT, Michael
:

class ListeObjetsBase
{
private:
std::vector<ObjetBase*> liste;
protected:


virtual bool ObjetOK (ObjetBase * objet)= 0;

void AddObject(ObjetBase * objet)
{
assert (ObjetOK (objet));

liste.push_back(objet);
}

};

class ListeObjetsThumb : public ListeObjetsBase
{
public:


bool ObjetOK (ObjetBase * objet)
{
return dynamic_cast <ObjetThumb*> (objet) != 0;
}

Avatar
Cyrille
On Fri, 20 Jan 2006 13:46:31 +0100, Cyrille :

try
{
ObjetThumb * thumb = dynamic_cast<ObjetThumb *>( obj );


dynamic_cast<> sur un pointeur ne lance pas d'exception. Il renvoie
éventuellement un pointeur nul.


Ouaip. Au temps pour moi.

--
Réhabilitez Garfieldd!
http://minilien.com/?HpC5r1qClU


Avatar
dagecko
Ouaip. Au temps pour moi.


J'aime bien cette façon d'écrire. Ça fait trop littéraire.

Au temps en en porte le vends

1 2