Polymorphism in C ++, type loss in a parent class vector

I have class A, which is the parent for classes B and C. And class X, which is the parent for Y and 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<A> 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)); } } 

Is it possible for objects to print the same result as hard-coded calls? What does it mean that I will consider them as their original type, and not its parent type? or as soon as I put them int with vector A, will they always be of type A?

+3
source share
2 answers

What you see is Cropping objects .
You store an object of the Derived class in a vector, which should store objects of the base class, this leads to the separation of objects, and the elements of the class related to the derived class will be cut, so the object stored in the vector simply acts as an object of the base class.

Decision:

You must save a pointer to an object of class Base in the vector:

 vector<X*> 

By storing the pointer to the base class, there will be no slicing, and you can also achieve the desired polymorphic behavior by executing virtual functions.
The right approach is to use a suitable smart pointer instead of storing the raw pointer in a vector. This ensures that you do not need to manually manage the memory; RAII will do this automatically.

+6
source

This is called slicing . When you push_back your elements to std::vector<A> , it basically copies elements to newly created instances of A Therefore, the part of the object that comes from the derived class will be lost ("cut").

To avoid slicing, you need to use a container that stores pointers instead of elements, so you should use std::vector<A*> <A *> or if your elements are a bunch, preferably selected by a vector of some kind of smartpointer ( std::shared_ptr or std::unique_ptr in C ++ 11, boost::shared_ptr or std::tr1::shared_ptr otherwise).

However, your code will not work as it is written, even if you change this: X , Y and Z all take their parameter by value, while all the elements in your vector will be of type A* , so dereferencing them will give A , therefore it will invoke the wrong method anyway. This could be solved by changing the signatures to always take A& or A* and use dynamic_cast to try to do this in type:

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

Of course, dynamic_cast is quite expensive, but if this is a problem, you might want to rethink your design. In addition, you need to ensure that A, B, C contain some virtual methods (a virtual destructor would be a good idea here), since otherwise dynamic_cast would not work)

+1
source

All Articles