Ryoshi217 Ryoshi217 - 11 days ago 5
C++ Question

How come accessing derived class member in base class pointer vector returns an error?

Streamlined Example of the problem:

#include <string>
#include <deque>
#include <iostream>

class Action{
public:
std::string name;

Action(std::string name){
this->name = name;
}
};

class Ability : public Action{
public:
int bar;
Ability(std::string name) : Action(name){}
};

int main(){
std::deque<Action*> foo;

Ability test("asdf");
test.bar = 122;

foo.push_back(&test);
std::cout << foo.at(0)->bar << std::endl;
return 0;
}


This creates an error, that there is no 'bar' member of 'Action'.

I realise that this relates to object slicing and I've attempted to use pointers, which allows the vector to push back the 'Ability' object but I cannot access its 'bar' member.

What am I missing?

Answer

First, a word from our sponsor: What is object slicing?

Now that you've read the above link, you can see that no slicing has taken place because the object was not copied into foo, only a pointer to the object was copied. The Ability is still intact, wherever in memory test sits.

But... Foo contains pointers to Action, not Ability. There is no way for a user of Foo to know if any given element of Foo is a reference to an Action, an Ability, or some other subclass of Action that they know absolutely nothing of. Very powerful stuff, the ability to work with something you don't even know exists, but this comes at a price: You have to work with it as something you do know. Users of Foo can only use the interface presented to them, that of Action. There are ways around this, such as dynamic_cast, but in most cases it best to stick with the provided interface and allow an overloaded method or operator to do black magic behind the scenes to do the correct thing for whatever the Action represents. If this means you have to

class Action{
    public:
    std::string name;

    Action(std::string name){
        this->name = name;
    }
    virtual int getbar() = 0; // pure virtual method that all subclasses  
                              // of Action must implement
};

class Ability : public Action{
public:
    int bar;
    Ability(std::string name) : Action(name){}
    int getbar()
    {
        return bar;
    }
};

and later

std::cout << foo.at(0)->getbar() << std::endl;

so be it.