polymorphism en c ++, perte de type dans le vecteur de la classe parente

J’ai une classe A, qui est un parent pour les classes B et C. Et une classe X, qui est un parent pour Y et Z.

class A {}; class B : public A {}; class C : public A {}; class X { void foo(A) { std:: cout << "A"; } }; class Y : public X { void foo(B) {std::cout << "B"; } }; class Z : public X { void foo(c) {std<<cout <<"C"; } }; int main() { B b; C c; Y y; Z z; y.foo(b);//prints B // b is a B, and Y::foo takes a B, hence print B y.foo(c);//prints A // mismatch between types, fall back and print A z.foo(b);//prints A // mismatch between types, fall back and print A z.foo(c);//prints C // c is a C, and Y::foo takes a C, hence print C std::vector v; v.push_back(b); v.push_back(c); //In this loop, it always prints A, but *this is what I want to change* for (size_t i = 0; i < v.size(); ++i) { z.foo(v.at(i)); y.foo(v.at(i)); } } 

Est-il possible d’obtenir les éléments pour imprimer le même résultat que les appels codés en dur? Cela signifie-t-il que je vais les traiter comme leur type d’origine, et non comme leur type parent? ou une fois que je les mets dans un vecteur de A ils seront toujours de type A?

Ce que vous voyez, c’est le découpage d’object .
Vous stockez l’object de la classe dérivée dans un vecteur censé stocker les objects de la classe base. Cela entraîne le découpage en object et les membres spécifiques à la classe dérivée de l’object en cours de stockage sont découpés en tranches; l’object stocké dans le vecteur agit object de classe de base.

Solution:

Vous devriez stocker le pointeur sur l’object de la classe de base dans le vecteur:

 vector 

En stockant un pointeur sur la classe de base, il n’y aurait pas de découpage en tranches et vous pouvez également obtenir le comportement polymorphe souhaité en rendant les fonctions virtual .
La bonne approche consiste à utiliser un pointeur Smart approprié au lieu de stocker un pointeur brut dans le vecteur. Cela vous évitera de gérer manuellement la mémoire. RAII le fera automatiquement pour vous.

Ceci s’appelle le tranchage . Lorsque vous push_back vos éléments dans un std::vector il les copie essentiellement dans des instances nouvellement construites de A Par conséquent, la partie de l’object de la classe dérivée sera perdue (“découpée en tranches”).

Afin d’éviter de découper en tranches, vous devez utiliser un conteneur qui stocke des pointeurs au lieu d’éléments. Vous devez donc utiliser un std::vector ou, si vos éléments sont alloués au tas, de préférence un vecteur d’une sorte de smartpointer ( std::shared_ptr ou std::unique_ptr en C ++ 11, boost::shared_ptr ou std::tr1::shared_ptr sinon).

Cependant, votre code ne fonctionnera pas tel qu’écrit, même si vous le modifiez: X , Y et Z prennent tous leur paramètre par valeur, alors que tous les éléments de votre vecteur auront le type A* , aussi, leur appellera toujours la mauvaise méthode. Cela pourrait être résolu en changeant les signatures pour toujours prendre A& ou A* et en utilisant dynamic_cast pour essayer de transtyper cela dans le type:

 class X { void foo(A*) { std:: cout << "A"; } }; class Y : public X { void foo(A* p) { if ( dynamic_cast(p) ) std::cout << "B"; // requires virtual methods in A else X::foo(p); } }; class Z : public X { void foo(A*){ if ( dynamic_cast(p) ) std::cout << "C"; // requires virtual methods in A else X::foo(p); } }; 

Bien sûr, dynamic_cast est un peu coûteux, mais si cela pose un problème, vous voudrez peut-être repenser votre conception. De plus, vous devez vous assurer que A, B, C contient virtual méthodes virtual (un virtual destructor serait de toute façon une bonne idée ici), car sinon dynamic_cast ne fonctionnerait pas)