1

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?

Ryoshi217
  • 13
  • 2
  • There's no object slicing happening here. It's simply the static type system behaving as it should. How is the compiler supposed to know that `foo.at(0)`, of type `Action*`, actually points to an instance of `Ability`, and not just a plain `Action` or another class derived from `Action`? – Igor Tandetnik Nov 26 '16 at 03:34
  • There's no such feature in C++ as accessing derived class's data member through a pointer of base class type. Slicing is not a problem here (and it does not happen). – AnT stands with Russia Nov 26 '16 at 05:06

1 Answers1

0

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.

Community
  • 1
  • 1
user4581301
  • 33,082
  • 7
  • 33
  • 54
  • Thanks, I should of structured my words better, in that I used pointers in an effort to prevent slicing. – Ryoshi217 Nov 26 '16 at 07:44