user1855952
user1855952

Reputation: 1575

C++ casting non-pointer instances of a custom class

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++.

Upvotes: 3

Views: 3717

Answers (2)

M.M
M.M

Reputation: 141554

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;
} 

Upvotes: 4

L&#225;szl&#243; Papp
L&#225;szl&#243; Papp

Reputation: 53155

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.

Upvotes: 2

Related Questions