question de débutant, casts & héritage

question de débutant, casts & héritage - C++ - Programmation

Marsh Posté le 25-02-2008 à 17:57:54    

Bonjour,
je suis super débutant en c++ ... quelqu'un pourrait-il m"expliquer pourquoi quand je fait ce code :

 
Code :
  1. class A {
  2. public:
  3. void set_pos(int x, int y) {
  4. _x=x;
  5. _y=y;
  6. };
  7. int _aa;
  8. private:
  9. int _x;
  10. int _y;
  11. };
  12. class B : public A {
  13. public:
  14. void set_pos2(int x) {
  15. _bb=x;
  16. }
  17. private:
  18. int _bb;
  19. };
  20. int main(int argc, char *argv[]) {
  21.   A *un = new A;
  22.   A *deux = new B;
  23.   //un = deux;
  24.   deux->set_pos2(1);
  25.   return 0;
  26. }
 

j'obtiens l'erreur suivante :

 
Citation :


error: 'class A' has no member named 'set_pos2'

 

en fait je ne comprend pas pourquoi, si on fait un new B, on ne peut pas appeler une méthode de cette classe parce qu'un pointeur du type de la classe mere pointe dessus.

 

merci par avance


Message édité par in_your_phion le 25-02-2008 à 18:03:17
Reply

Marsh Posté le 25-02-2008 à 17:57:54   

Reply

Marsh Posté le 25-02-2008 à 18:04:40    

'deux' est de type A, donc pas de méthode set_pos2() qui n'est présente que dans B.


---------------
-- Debian -- Le système d'exploitation universel | Le gras c'est la vie! | /(bb|[^b]{2})/
Reply

Marsh Posté le 25-02-2008 à 18:06:54    

dwogsi a écrit :

'deux' est de type A, donc pas de méthode set_pos2() qui n'est présente que dans B.

 

pourtant j'ai déjà vue plusieurs fois l'ecriture du type

Code :
  1. A * un = new B;
 

avec B qui dérive de A.
quel serait l'intérêt ?


Message édité par in_your_phion le 25-02-2008 à 18:07:25
Reply

Marsh Posté le 25-02-2008 à 18:10:57    

Ba dans ma logique, on devrait toujours savoir de quel type est une variable. On évite ainsi d'appeler des méthodes qui n'existent pas dans l'objet par exemple.
 
Donc je serais tenté de te dire qu'une variable de type A ne peut contenir qu'un objet de type A (ça parait évident...).
 
Cela-dit, je ne suis pas un spécialiste, faudrait que quelqu'un confirme/infirme.


Message édité par dwogsi le 25-02-2008 à 18:11:22

---------------
-- Debian -- Le système d'exploitation universel | Le gras c'est la vie! | /(bb|[^b]{2})/
Reply

Marsh Posté le 25-02-2008 à 19:06:34    

google("polymorphisme" )

Reply

Marsh Posté le 25-02-2008 à 20:20:50    

l'intérêt c'est par exemple d'avoir un vector de pointeur sur A que tu remplis de manière aveugle avec des
instances de filles de A pour ensuite appeler polymorphiquement des méthodes de l'interface de A.

 

exemple de mon cours.

 
Code :
  1. #include <iostream>
  2. #include <vector>
  3. using namespace std;
  4. class Vehicule
  5. {
  6.   public :
  7.   virtual ~Vehicule() {}
  8.   virtual void demarrer() = 0;
  9. };
  10. class Voiture : public Vehicule
  11. {
  12.   virtual ~Voiture() {}
  13.   virtual void demarrer()
  14.   {
  15.    cout << "demarrage Voiture" << endl;
  16.   }
  17. };
  18. class Camion : public Vehicule
  19. {
  20.   virtual ~Camion() {}
  21.   virtual void demarrer()
  22.   {
  23.    cout << "demarrage Camion" << endl;
  24.   }
  25. };
  26. class Bateau : public Vehicule
  27. {
  28.   virtual ~Bateau() {}
  29.   virtual void demarrer()
  30.   {
  31.    cout << "demarrage Bateau" << endl;
  32.   }
  33. };
  34. int main()
  35. {
  36.   vector<Vehicule*> parc;
  37.  
  38.   parc.push_back( new Voiture );
  39.   parc.push_back( new Camion );
  40.   parc.push_back( new Bateau );
  41.   parc.push_back( new Voiture );
  42.   parc.push_back( new Bateau );
  43.   parc.push_back( new Camion );
  44.   parc.push_back( new Bateau );
  45.   parc.push_back( new Camion );
  46.  
  47.   for( vector<Vehicule*>::iterator it=parc.begin();
  48.        it != parc.end();
  49.        it++
  50.      )
  51.   {
  52.     (*it)->demarrer();
  53.   }
  54.   return 0;
  55. }
 


L'intérêt de l'héritage est de FACTORISER les comportements. Donc en général la classe mère expose une interface
commune à toutes ces filles.

Message cité 1 fois
Message édité par Joel F le 26-02-2008 à 14:44:17
Reply

Marsh Posté le 26-02-2008 à 11:12:50    

Joel F a écrit :

l'intérêt c'est par exemple d'avoir un vector de pointeur sur A que tu remplis de manière aveugle avec des
instances de filles de A pour ensuite appeler polymorphiquement des méthodes de l'interface de A.

 

exemple de mon cours.

 
Code :
  1. #include <iostream>
  2. #include <vector>
  3. using namespace std;
  4. class Vehicule
  5. {
  6.   public :
  7.   virtual void demarrer() = 0;
  8. };
  9. class Voiture : public Vehicule
  10. {
  11.   virtual ~Voiture() {}
  12.   virtual void demarrer()
  13.   {
  14.    cout << "demarrage Voiture" << endl;
  15.   }
  16. };
  17. class Camion : public Vehicule
  18. {
  19.   virtual ~Camion() {}
  20.   virtual void demarrer()
  21.   {
  22.    cout << "demarrage Camion" << endl;
  23.   }
  24. };
  25. class Bateau : public Vehicule
  26. {
  27.   virtual ~Bateau() {}
  28.   virtual void demarrer()
  29.   {
  30.    cout << "demarrage Bateau" << endl;
  31.   }
  32. };
  33. int main()
  34. {
  35.   vector<Vehicule*> parc;
  36.  
  37.   parc.push_back( new Voiture );
  38.   parc.push_back( new Camion );
  39.   parc.push_back( new Bateau );
  40.   parc.push_back( new Voiture );
  41.   parc.push_back( new Bateau );
  42.   parc.push_back( new Camion );
  43.   parc.push_back( new Bateau );
  44.   parc.push_back( new Camion );
  45.  
  46.   for( vector<Vehicule*>::iterator it=parc.begin();
  47.        it != parc.end();
  48.        it++
  49.      )
  50.   {
  51.     (*it)->demarrer();
  52.   }
  53.   return 0;
  54. }
 


L'intérêt de l'héritage est de FACTORISER les comportements. Donc en général la classe mère expose une interface
commune à toutes ces filles.

 

salut,
merci pour ta réponse. Je comprend bien l'exemple, et je pense voire l'interêt du truc ? c'est a dire que c'est générique. Cependant, je ne comprend toujours pas bien pourquoi dans certains cas on fait

 
Code :
  1. A * un = new B;


avec B qui dérive de A, alors qu'on ne peut manifestement pas accéder aux champs de B (qui ne sont pas contenus dans A) par la suite  :(

 

ps : les destructeurs virtuels sont-ils nécessaires dans l'exemple ?


Message édité par in_your_phion le 26-02-2008 à 11:16:33
Reply

Marsh Posté le 26-02-2008 à 11:22:15    

c'est pourtant bien ce que je fais ici , je mets un B* dans des A*.
Pour savoir de quelles types effectif un A* est, on utilise dynamic_cast.
 

Code :
  1. A*p = new B;
  2. // ... plein de code, on sait plus ce qui il y a dans A* p
  3. B* pb;
  4. if( pb = dynamic_vast<B*>(p) )
  5. {
  6.   cout << "p est de type B" << endl;
  7. }
  8. else // p n'etait pas de type B, pb vaut donc NULL
  9. {
  10.   cout << "p n'est pas de type B*" << endl;
  11. }


 
Les destructeurs virtuels assurent la présence et la prise en compte de la table de méthodes virtuelles. Il est de bon ton de les laisser comme ça ;)

Reply

Marsh Posté le 26-02-2008 à 12:19:32    

ok, merci  :jap:

 

alors si j'ai bien tout lu Freud, je comprend que quand on fait :

Code :
  1. A * ab = new B;


avec B qui dérive de A, c'est qu'on fait du polymorphisme et que donc que A utilise au moins une fonction virtuelle. Autrement ca n'a aucun intérêt, non ?

 
Joel F a écrit :


Les destructeurs virtuels assurent la présence et la prise en compte de la table de méthodes virtuelles. Il est de bon ton de les laisser comme ça ;)

 

mais dans cet exemple, ca ne sert a rien de les mettre non ?  :??:


Message édité par in_your_phion le 26-02-2008 à 12:20:19
Reply

Marsh Posté le 26-02-2008 à 13:45:20    

Comme tu fais un new d'un type B, il serait logique de faire un delete d'un type B.
Or si tu fais un delete sur "ab" (dans l'exemple), s'il n'a pas de destructeur virtuel il utilisera le destructeur de A et non de B.
 
En gros, pour résumer ne pas avoir de destructeur virtuel c'est comme faire :

delete (A*)new B;


 
Sinon dans l'exemple comme Vehicule n'a pas de destructeur virtuel, même si y en a dans les classes filles, ca sera toujours celui de Vehicule qui sera appelé.

Reply

Marsh Posté le 26-02-2008 à 13:45:20   

Reply

Marsh Posté le 26-02-2008 à 14:43:53    

oops en effet, je corrige.

Reply

Marsh Posté le 27-02-2008 à 10:58:34    

Tarabiscote a écrit :

Comme tu fais un new d'un type B, il serait logique de faire un delete d'un type B.
Or si tu fais un delete sur "ab" (dans l'exemple), s'il n'a pas de destructeur virtuel il utilisera le destructeur de A et non de B.
 
En gros, pour résumer ne pas avoir de destructeur virtuel c'est comme faire :

delete (A*)new B;


 
Sinon dans l'exemple comme Vehicule n'a pas de destructeur virtuel, même si y en a dans les classes filles, ca sera toujours celui de Vehicule qui sera appelé.


 
ok, merci :)  
ce que je voulais dire, c'est que dans l'exemple de Joel F. il faudrait un destructeur virtuel pour la classe de base, mais les destructeurs virtuels dans les classes dérivées (les non abstraites dans l'exemple) ne servent à rien (sauf si on dérive ces classes)
 
j'ai bon ? :sweat:  
 
 
 
 

Reply

Marsh Posté le 27-02-2008 à 13:27:07    

oui, mais comme disait feu Bruno Garcia,
"virtual un jour, virtual toujours"

 

les mettre virtual alors que ca a part l'air de servir t'évitera des incongruités le jour ou tu dérivera de Voiture pour faire VoitureAmphibie et que tu auras oublié le virtual sur le destructeur

Message cité 1 fois
Message édité par Joel F le 27-02-2008 à 13:27:25
Reply

Marsh Posté le 27-02-2008 à 15:24:51    

Joel F a écrit :

oui, mais comme disait feu Bruno Garcia,
"virtual un jour, virtual toujours"
 
les mettre virtual alors que ca a part l'air de servir t'évitera des incongruités le jour ou tu dérivera de Voiture pour faire VoitureAmphibie et que tu auras oublié le virtual sur le destructeur


 
ok, merci  :jap: cependant j'ai lu (je ne sais plus où) qu'il ne fallait pas rajouter de destructeurs virtual par défaut car ca ralenti l'execution

Reply

Marsh Posté le 27-02-2008 à 16:13:37    

oui en  1878 avec gcc 0.0.1 c'était le cas mais depuis l'avènement des vrais compilo ... ça n'a aucun sens de dire ça.

Reply

Marsh Posté le 27-02-2008 à 17:46:26    

Joel F a écrit :

oui en  1878 avec gcc 0.0.1 c'était le cas mais depuis l'avènement des vrais compilo ... ça n'a aucun sens de dire ça.

 

lol

 

ok!  :)

 

ps : mais je croyais que virtual & co c'etait "late binding", donc décidées au moment de l'execution (donc apres compilation); me trompe-je ?


Message édité par in_your_phion le 27-02-2008 à 17:50:18
Reply

Marsh Posté le 27-02-2008 à 18:10:45    

non tu ne te trompes pas. Ma remarque portait sur le fait que cette LU du virtual qui coute date un peu des compilos foireux genre 2.95.2 et antérieur.

Reply

Sujets relatifs:

Leave a Replay

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