3

Lets say I have class A and it has a subclass B.

class A 
{
public:
    A(){};
}

class B : public A
{
public:
    B(int value){ foo = value };
    int foo;
}

B is a subclass of A with an int foo.

Okay now lets say I do the following:

std::vector<A> mylist;
B myB(5); //Create a new instance of class B
mylist.push_back(myB); //Add b to my vector of A.

Now I want to access an element of mylist and type cast it to type B and retrieve 'foo'. Is the following legal?

B *instance = (B *)&mylist[0]; 

I've tried doing this in my project but if I were to print out the value of foo it would be incorrect:

std::cout<<instance->foo<<std::endl; // This does not give me 5. It gives me a garbage number.

The problems stems from the fact that I don't know the proper way to cast non-pointers in C++.

user1855952
  • 1,515
  • 5
  • 26
  • 55
  • `Okay now lets say I do the following:` Don't. Since you shouldn't do that, the rest of your question becomes a moot point. – PaulMcKenzie Apr 06 '14 at 03:02
  • 1
    ^Well then I'm trying to figure out the right way to cast a non-pointer... – user1855952 Apr 06 '14 at 03:03
  • possible duplicate of [What is the slicing problem in C++?](http://stackoverflow.com/questions/274626/what-is-the-slicing-problem-in-c) – n. m. could be an AI Apr 06 '14 at 03:03
  • If your vector holds A objects, then it only is supposed to know about A objects. Let's say you now change the vector to hold pointers to A objects. Still, the vector should only know about A pointers and you should work solely with the A interface. So what I'm saying is that you should rethink your design. Maybe introduce virtual functions. – PaulMcKenzie Apr 06 '14 at 03:07
  • There is no right way. – n. m. could be an AI Apr 06 '14 at 03:07

2 Answers2

4

When you write mylist.push_back(myB), a slice occurs. The mylist stores A objects. So what happens is that your B is converted to an A, losing all of the things that are in B and not A. Just the A is stored. You cannot retrieve the lost pieces.

It is the same as doing:

B myB(5);
A a(myB);

In order to have polymorphic behaviour, you need to make A be a polymorphic class, and also refer to the objects by pointers, i.e. A * .

So you need to add at least one virtual function to A (virtual destructor would be a good idea), and have your vector store pointers. For example:

std::vector< A* > mylist;
mylist.push_back( new myB(5) );
B *instance = dynamic_cast<B *>(mylist[0]);

Now, this code is OK but you have to be very careful about deleting memory you new'd. To solve that for you there is shared_ptr which is a ref-counted pointer. I made the following test case which seems to work (disclaimer - I'm not an expert in polymorphism with smart pointers, so I hope there is not a stealthy error here)

#include <iostream>
#include <memory>
#include <vector>

class A 
{
public:
    A(){};
    virtual ~A() {}
};

class B : public A
{
public:
    B(int value): foo(value) {}
    int foo;
    ~B() { std::cout << "Destructor B(" << foo << ")" << std::endl; }
};

int main()
{
    std::vector< std::shared_ptr<A> > mylist;

// Test the polymorphic behaviour
//  mylist.push_back( std::make_shared<A>() );

// create directly into container
    mylist.push_back( std::make_shared<B>(5) );

// via pointer
    B *temp_b = new B(6);
    mylist.push_back( std::shared_ptr<B>(temp_b) );

    std::shared_ptr<B> instance  = std::dynamic_pointer_cast<B>( mylist[0] );
    if ( !instance )
       std::cout << "Item was not a B" << std::endl;
    else
       std::cout << instance->foo << std::endl;
} 
M.M
  • 138,810
  • 21
  • 208
  • 365
2

This is a typical example of slicing issues in C++.

You should store pointer in the vector to easily avoid them. If you do not wanna to deal with raw pointers, use smart pointers.

László Papp
  • 51,870
  • 39
  • 111
  • 135