problème d'héritage

problème d'héritage - C++ - Programmation

Marsh Posté le 05-08-2010 à 12:31:16    

bonjour,  
 
j'ai une problème  :whistle: en fait:  
 
- j'ai deux classes A B  
 
-  B hérite de A
 
-  une classe C qui hérite de A
 
-  une classe D qui hérite de C
 
schématiquement:
 
A <--- B
|
|
C <--- D
 
Or, je voudrais faire hériter D de B, soit :
 
A <--- B
|        |
|        |
C <--- D
 
le problème, c'est que D hérite de C qui contient donc A, et comme B contient A, ben si D hérite de B il contient potentiellement A deux fois...  :pt1cable:  :pt1cable:  :pt1cable:  
 
comment faire ??
 
merci par avance!
 

Reply

Marsh Posté le 05-08-2010 à 12:31:16   

Reply

Marsh Posté le 05-08-2010 à 13:18:39    

Renseigne-toi sur l'heritage virtuel (si B&C heritent virtuellement de A, il n'y en aura qu'un dans D).


---------------
The truth is rarely pure and never simple (Oscar Wilde)
Reply

Marsh Posté le 05-08-2010 à 13:48:34    

Un Programmeur a écrit :

Renseigne-toi sur l'heritage virtuel (si B&C heritent virtuellement de A, il n'y en aura qu'un dans D).


 
merci!!  :)

Reply

Marsh Posté le 06-08-2010 à 11:37:34    

up

 

ne fait j'aurais encore une question ...

 

comment je fais si A, B et C prennent des paramètres en entrée ??

 

par exemple A(const char * buffer)

 

et :  B(const char *buffer, int toto) C(const char *buffer, int toto)

 

buffer c'est la même chose pour tous.

 

une fois que j'initialise D, je dois initialisée deux fois ??

 
Code :
  1. D(const char * buffer, int toto)
  2. : A(buffer)
  3. , B(buffer,toto)
  4. , C(buffer,toto)
  5. { ; }
 


c'est comme ça ? dans ce cas je passe buffer 3 fois pour des constructeurs qui utilisent le même paramètre ...

 

merki par avance


Message édité par in_your_phion le 06-08-2010 à 11:37:52
Reply

Marsh Posté le 06-08-2010 à 11:40:55    

Oui: le constructeur de A sera appele une fois, et les parametres seront ceux donne par le constructeur de la classe la plus derivee (D ici).  C'est piegeux si A a un constructeur par defaut mais qu'il ne faut pas l'utiliser, si il n'y a pas d'appel a un constructeur de A dans la classe la plus derivee, c'est le constructeur par defaut qui sera appele.


---------------
The truth is rarely pure and never simple (Oscar Wilde)
Reply

Marsh Posté le 07-08-2010 à 14:03:12    

ok merci!!

 

Y'a t-il un moyen d'éviter l'héritage virtuel ? je trouve ça vraiment complexe à mettre en œuvre et piegeux comme tu le dis ;)

 

Que faut t'il faire dans ce genre de situation ?

 
Code :
  1. A_1 <-------- A_2
  2.   |            |
  3.   |            |
  4.   |            |
  5. B_1 <-------- B_2
  6.   |            |
  7.   |            |
  8.   |            |
  9. C_1 <-------- C_2
  10.   .            .
  11.   .            .
  12.   .            .
 

i.e :  

 

- C_1 dérive de B_1 qui dérive de A_1 - etc
- C_2 dérive de B_2 qui dérive de A_2 - etc

 

chaque classe A_1,B_1, ... a besoin d'hériter d'une classe correspondante A_2, B_2, ...

 

j'ai l'impression que ce genre de situation peut arriver souvent .... Par exemple :

 

A_1 est une voiture traban qui 'hérite' d'une classe A_2 qui implémente un moteur de base (à pédales  :D )

 

B_1 est une voiture hummer qui 'hérite' d'une classe B_2 qui implémente un moteur à réaction

 

ne reste t-il que l'héritage virtuel et tout le hardcore qui va avec ?  [:tinostar]

 

merci par avance ...    


Message édité par in_your_phion le 07-08-2010 à 14:10:45
Reply

Marsh Posté le 07-08-2010 à 20:22:21    

J'ai tendance à penser que l'héritage virtuel, ca arrive plutôt rarement. Regarde si tu ne peux pas découper ton objet en des entités plus simples, essaye de séparer en terme de responsabilités. Est-ce que ta voiture doit nécessairement savoir exactement quel type de moteur elle embarque ? Ou est-ce qu'au contraire, il lui suffit d'avoir une interface basique sur comment l'utiliser et ne pas se soucier de son type exact ?


---------------
last.fm
Reply

Marsh Posté le 07-08-2010 à 21:57:59    

Une voiture qui hérite (virtuellement ou non) d'un moteur, ça m'inquiète.
 
L'héritage virtuel arrive naturellement pour les classes qui ont la fonction des interfaces de Java.


---------------
The truth is rarely pure and never simple (Oscar Wilde)
Reply

Marsh Posté le 07-08-2010 à 22:12:56    

fichtre, oui composition/aggrégation et non héritage ici

Reply

Marsh Posté le 08-08-2010 à 12:58:59    

Un Programmeur a écrit :

Une voiture qui hérite (virtuellement ou non) d'un moteur, ça m'inquiète.


 
hello
oui l'exemple est peut être mal choisi, mais c'était pour illustrer.
 
mon probleme est que ma classe A_1 de base hérite d'un encodeur, qui permet d'encoder certaines données de base (type entier, flottant, etc)
 
ma classe B_1, qui hérite de A_1, aurait besoin d'un encodeur un peu plus sophistiqué, qui pemet d'encoder des données plus haut niveau. Donc mon encodeur sophistiqué est un type d'encodeur qui a quelques fonctions en plus (encoder un prix, un produit, etc)
 
Si je fais de l'agregation ou de la composition pour moi ça reviens au même: on a toujours duplication des données, ou alors je suis obligé de faire un static_cast dans ma classe B_1 mais là je perds l'intéret de l'héritage :
 

Code :
  1. class A_1 {
  2. ...
  3. private:
  4. Encoder * e;
  5. };
  6. class B_1 : public A_1 {
  7. ...
  8. private:
  9. EncoderSophistique * e;
  10. };


 
C'est peut être mieux au niveau de la sémantique, ok, mais bon ..
 
Du coup je ne vois pas du tout comment découper autrement  :??: La seule solution 'mauvaise' que je trouve, c'est de faire un encodeur énorme avec tout dedans, et chaque classe est responsable de ce qu'elle fait avec. Je vois pas d'autre option...


Message édité par in_your_phion le 08-08-2010 à 13:00:27
Reply

Marsh Posté le 08-08-2010 à 12:58:59   

Reply

Marsh Posté le 09-08-2010 à 15:49:47    

Tu peux regarder du coté du design pattern "decorator", c'est une solution pour éviter l'héritage multiple.

Reply

Marsh Posté le 09-08-2010 à 23:44:50    

Paulp a écrit :

Tu peux regarder du coté du design pattern "decorator", c'est une solution pour éviter l'héritage multiple.


 
hello,
merci! mais je ne crois pas que ce soit adapté pour ce que je veux faire.
 
En fait, je crois que j'ai trouvé, il faut que j'utilise la covariance des types de retour avec des pointeurs dans mes classes. Du style :
 
mon problème :
 

Code :
  1. A_1 <-------- EncodeurBasique
  2.   ^            ^
  3.   |            |
  4.   |            |
  5. B_1 <-------- EncodeurDeOuf
  6.   .            .
  7.   .            .
  8.   .            .
  9. etc ...


 
et on veut pas utiliser l'héritage virtuel car c'est trop ba-caca-pas-beau-et-un-calvaire.
 
La solution, s'il en fut :
 

Code :
  1. #include <iostream>
  2. using namespace std;
  3. struct EncodeurBasique {
  4. void dumpBasique() { cout << "encodeur basique" << endl; }
  5. };
  6. struct EncodeurDeOuf : public EncodeurBasique {
  7. void dumpDeOuf() { cout << "encodeur de ouf: tu peux pas teste" << endl; }
  8. };
  9. struct A_1 {
  10. virtual E_1* getE() { return new EncodeurBasique; }
  11. };
  12. struct B_1 : public A_1 {
  13. E_2* getE() { return new EncodeurDeOuf; }
  14. };
  15. int main() {
  16. A_2 * a2 = new A_2;
  17. a2->getE()->dumpDeOuf();
  18. return 0;
  19. }


 
comme ça chaque classe a son encodeur, et chaque encodeur de ouf dérive d'un encodeur plus basique.
 
c'est bon ça, non ?  [:tinostar]

Message cité 1 fois
Message édité par in_your_phion le 09-08-2010 à 23:51:47
Reply

Marsh Posté le 10-08-2010 à 00:29:59    

Pourquoi tu n'as pas de hierarchie entre les encodeurs ?

Code :
  1. class EncodeurBasique {
  2.   virtual void dump() {
  3.     cout << "encodeur de base" << endl;
  4.   }
  5. }
  6. class EncodeurComplexe : public EncodeurBasique {
  7.   void dump() {
  8.     cout << "encodeur complexe" << endl;
  9.   }
  10. }


 
Ensuite tu ajoutes un membre dans A_1 pointant vers un objet EncodeurBasique que tu initialise avec le bon encodeur suivant si tu es A, B, ... (ou alors tu le passes en parametre)
 
Tu peux aussi definir une classe abstraite pure pour definir "l'interface" des encodeurs si tu ne souhaites pas avoir de hierarchie entre tes differents encodeurs (par exemple parce qu'il n'ont rien a voir dans leur fonctionnement interne).

Message cité 1 fois
Message édité par mr simon le 10-08-2010 à 00:36:42
Reply

Marsh Posté le 10-08-2010 à 10:13:48    

in_your_phion a écrit :


 
hello,
merci! mais je ne crois pas que ce soit adapté pour ce que je veux faire.
 
En fait, je crois que j'ai trouvé, il faut que j'utilise la covariance des types de retour avec des pointeurs dans mes classes. Du style :
 
mon problème :
 

Code :
  1. A_1 <-------- EncodeurBasique
  2.   ^            ^
  3.   |            |
  4.   |            |
  5. B_1 <-------- EncodeurDeOuf
  6.   .            .
  7.   .            .
  8.   .            .
  9. etc ...


 
et on veut pas utiliser l'héritage virtuel car c'est trop ba-caca-pas-beau-et-un-calvaire.
 
La solution, s'il en fut :
 

Code :
  1. #include <iostream>
  2. using namespace std;
  3. struct EncodeurBasique {
  4. void dumpBasique() { cout << "encodeur basique" << endl; }
  5. };
  6. struct EncodeurDeOuf : public EncodeurBasique {
  7. void dumpDeOuf() { cout << "encodeur de ouf: tu peux pas teste" << endl; }
  8. };
  9. struct A_1 {
  10. virtual E_1* getE() { return new EncodeurBasique; }
  11. };
  12. struct B_1 : public A_1 {
  13. E_2* getE() { return new EncodeurDeOuf; }
  14. };
  15. int main() {
  16. A_2 * a2 = new A_2;
  17. a2->getE()->dumpDeOuf();
  18. return 0;
  19. }


 
comme ça chaque classe a son encodeur, et chaque encodeur de ouf dérive d'un encodeur plus basique.
 
c'est bon ça, non ?  [:tinostar]


 
Tes fonctions doivent avoir le même type de retour (donc ici, ton EncodeurBasique) et il faut que ton interface soit tout de même similaire entre tes deux encodeurs. Au sein de ta classe B_1, tu pourras peut-être supposer que ton encodeur est du type EncodeurComplexe ou je ne sais quoi et faire un static_cast pour accéder à ses méthodes spécifiques


---------------
last.fm
Reply

Marsh Posté le 10-08-2010 à 15:04:43    

hello,
 

mr simon a écrit :

Pourquoi tu n'as pas de hierarchie entre les encodeurs ?


 
je ne suis pas sur de comprendre ? Normalement si car mon encodeur complexe dérive de mon encodeur basique  
 

theshockwave a écrit :


Tes fonctions doivent avoir le même type de retour (donc ici, ton EncodeurBasique) et il faut que ton interface soit tout de même similaire entre tes deux encodeurs. Au sein de ta classe B_1, tu pourras peut-être supposer que ton encodeur est du type EncodeurComplexe ou je ne sais quoi et faire un static_cast pour accéder à ses méthodes spécifiques


 
Apparement non, je ne suis pas obligé de faire de static_cast si j'utilise les types de retour covariant ... :
 
http://www.lwithers.me.uk/articles/covariant.html
 

Reply

Marsh Posté le 12-08-2010 à 09:58:34    

bonjour,
j'aurais encore une question s'il vous plait :D

 

Est ce que je peux faire ça ?

 
Code :
  1. //EncodeurSophistique hérite de EncodeurDeBase
  2. class A {
  3. EncodeurDeBase * e; // initialisé à new EncodeurDeBase;
  4. };
  5. class B : public A {
  6. EncodeurSophistique * e; // initialisé à new EncodeurSophistique;
  7. };
 

est ce que ce n'est pas trop "crade" ? car pour la classe B j'initialise B::e mais du coup B hérite de A::e (en privé donc inaccessible) qui ne sera jamais utilisé ni initialisé.

 

merci par avance

Message cité 1 fois
Message édité par in_your_phion le 12-08-2010 à 10:00:25
Reply

Marsh Posté le 12-08-2010 à 15:08:34    

B n'a pas a hérité de A,mais d'une interface de A
 
class A : public AInterface
{
  SomeType memberOfA;
};
 
classBA : public AInterface
{
  SomeOtherType memberOfB;
}
 

Reply

Marsh Posté le 12-08-2010 à 17:20:55    

in_your_phion a écrit :

bonjour,
j'aurais encore une question s'il vous plait :D
 
Est ce que je peux faire ça ?
 

Code :
  1. //EncodeurSophistique hérite de EncodeurDeBase
  2. class A {
  3. EncodeurDeBase * e; // initialisé à new EncodeurDeBase;
  4. };
  5. class B : public A {
  6. EncodeurSophistique * e; // initialisé à new EncodeurSophistique;
  7. };


 
est ce que ce n'est pas trop "crade" ? car pour la classe B j'initialise B::e mais du coup B hérite de A::e (en privé donc inaccessible) qui ne sera jamais utilisé ni initialisé.
 
merci par avance


 
 
non, il n'en hérite pas en privé et A::e est toujours accessible en faisant ca :

Code :
  1. B b;
  2. b.A::e->SomeFunc;


 
Qui plus est, tu n'aurais pas de moyen d'accéder au bon e depuis les fonctions que tu pourrais vouloir factoriser dans A


---------------
last.fm
Reply

Marsh Posté le 12-08-2010 à 21:10:21    

theshockwave a écrit :

Qui plus est, tu n'aurais pas de moyen d'accéder au bon e depuis les fonctions que tu pourrais vouloir factoriser dans A

 

hello
dans ce cas je ne vois pas la solution, comment faire ?  :pt1cable:

 

En fait, pp à l'interface: comment, quand on a une classe :

 
Code :
  1. class A {
  2. public:
  3. E * e;
  4. };
 

pourquoi on peut faire :

 
Code :
  1. A::e->SomeFunc();
 

alors que 'e' ne pointe vers aucun objet de type E alloué ???

 

je ne pige plus ... il me semblait que pour faire ça il faut avoir un objet alloué. Par exemple :

 
Code :
  1. int * p;
  2. *p = 3; //normalement ça c'est caca ?? pourquoi pas avec la classe ????

Message cité 1 fois
Message édité par in_your_phion le 12-08-2010 à 23:45:52
Reply

Marsh Posté le 13-08-2010 à 17:54:51    

uuuup

Reply

Marsh Posté le 13-08-2010 à 19:09:42    

in_your_phion a écrit :


Code :
  1. A::e->SomeFunc();


[/cpp]


 
Ceci ne marche pas puisque "e" n'est pas statique et ce n'est ce qu'a ecris theshockwave:  

Code :
  1. B b;
  2. b.A::e->SomeFunc;

Reply

Marsh Posté le 16-08-2010 à 17:59:08    

mr simon a écrit :

 

Ceci ne marche pas puisque "e" n'est pas statique et ce n'est ce qu'a ecris theshockwave:

Code :
  1. B b;
  2. b.A::e->SomeFunc;


 

effectivement, ça me parait bizarre que ça marche. En plus de toute manière la fonction est privée donc ce n'est pas possible même si 'e' est statique, non ?

 

Est ce que quelqu'un pourrait m'indiquer la solution ? :) je ne vois pas ... i.e. comment faire de la composition ou de l'agrégation en évitant les static_cast partout, es-ce possible ?

 

merci  :hello:


Message édité par in_your_phion le 16-08-2010 à 18:00:05
Reply

Marsh Posté le 16-08-2010 à 18:04:51    

Code :
  1. struct iEncoder
  2. {
  3.   virtual void Encode() {};
  4. };
  5. struct iEncoderUser
  6. {
  7.   virtual iEncoder* GetEncoder() = 0;
  8.   void DoSomething()
  9.   {
  10.     GetEncoder()->Encode();
  11.   }
  12. };
  13. struct SimpleEncoder : public iEncoder
  14. {
  15. };
  16. struct ComplexEncoder : public iEncoder
  17. {
  18. };
  19. struct A : public iEncoderUser
  20. {
  21.   SimpleEncoder e;
  22.   virtual iEncoder* GetEncoder() {return &e;}
  23. };
  24. struct B : public iEncoderUser
  25. {
  26.   ComplexEncoder e;
  27.   virtual iEncoder* GetEncoder() {return &e;}
  28. };


 
tout le code générique (factorisable) doit être dans iEncoderUser, qui ne peut donc pas supposer que l'encodeur est d'un type ou d'un autre (donc pas de static cast).
A et B connaissent directement le type de leur encodeur, donc pas de cast non plus


Message édité par theshockwave le 16-08-2010 à 18:06:19

---------------
last.fm
Reply

Marsh Posté le 16-08-2010 à 18:50:46    

salut,
merci beaucoup pour ta réponse ...  
seulement, dans mon problème B dérive obligatoirement de A  :cry: du coup je suis obligé d'utiliser l'héritage virtuel non ?

Reply

Marsh Posté le 16-08-2010 à 19:01:28    

pourquoi l'interface commune ne te suffirait pas ?


---------------
last.fm
Reply

Marsh Posté le 16-08-2010 à 22:53:51    

theshockwave a écrit :

pourquoi l'interface commune ne te suffirait pas ?

 

salut,
en fait IEncoder est une interface pour les encodeurs, et IEncodeurUser une interface pour A et B, c'est ça ?

 

dans ton exemple, B n'hérite plus de A ... mais les deux A et B héritent d'une interface commune iEncoderUser. Dans mon cas B est "une sorte de" A.

 

La seule solution que je vois, ce serait effectivement de casser l'héritage et de dire que A et B sont disctincts et héritent chacun d'un encodeur différent. Mais bon, normalement B est un bien une sorte de A  :(

 

ou alors, de faire des fonctions vides dans comme dans ton interface IEncoder, mais c'est pas terrible non ? Comme ça alors ?

 
Code :
  1. struct IEncoder {
  2.   virtual int encodeInt() {return 0;}
  3.   virtual int encodePrice() {return 0; };
  4.   //...
  5.   //toutes les fonctions mais non implémentées
  6. };
  7. struct EncoderBasic : public IEncoder {
  8.   int encodeInt() { //réimplémente un encodage d'entier }
  9. };
  10. struct EncoderComplex : public EncoderBasic {
  11.   int encodePrice() { //réimplémente un encodage de Price }
  12. };
  13. struct IUser {
  14.   virtual IEncoder* getEncoder() =0;
  15.   IEncoder *e;
  16. };
  17. struct A : public IUser {
  18.     virtual IEncoder* getEncoder() {
  19.     e = new EncoderBasic;
  20.     return e;
  21.     }
  22. };
  23. struct B : public A {
  24.     IEncoder* getEncoder() { 
  25.     e = new EncoderComplex;
  26.     return e;
  27. }
  28. };
 

.. :??:  [:tinostar]  


Message édité par in_your_phion le 16-08-2010 à 23:21:41
Reply

Marsh Posté le 17-08-2010 à 10:38:54    

instancier dans ton getter me semble être une grosse erreur.
 
Si tu as ta classe A et que tu peux la modifier, alors tu peux la transformer en simple interface, tu dois bien pouvoir choisir qui est instancié, non ? Je soupçonne que cette contrainte que tu mets n'en est pas vraiment une. Quoiqu'il arrive, tu ne pourras pas faire quelque chose de propre si tu veux avoir B qui dérive de A.


---------------
last.fm
Reply

Marsh Posté le 17-08-2010 à 13:24:07    

salut
oui c'est clair  :o

 

finalement j'ai changé, effectivement je pourrais pas faire quelque chose de propre avec B qui dérive de A et EncodeurComplexe qui dérive de Encodeur.

 

J'ai finalement un seul encodeur qui fait les trucs vraiment de base (encodage int/string, etc), et j'ai transféré les encodages plus complexes dans les classes A et B, car les traitements sont davantage fonctionnels : il appartient donc aux classes de savoir ce qu'elles encodent, pas à l'encodeur.

 

Donc voilà, je ne sais pas si c'est la meilleur solution, mais bon, ça me simplifie les choses...

 

merci encore pour ton aide!  :)


Message édité par in_your_phion le 17-08-2010 à 13:24:35
Reply

Marsh Posté le    

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed