简体   繁体   中英

How can I achieve polymorphism in this simple minimal example?

The problem

I'm trying to achieve polymorphism by having types of Event subtypes inherit from the base class Event . The idea is that there is always an active event, denoted by the pointer in Runner ( Event* current_event ). When the Runner::step method is called, the Event::go method is called, which flips some switches and changes the current behaviour of the program. Then, the pointer is pointed at a different event until Runner::step is called again.

The issue is that the pointer is always using the base class Event versions of variables. It might be a simple fix but I'd be greatful if somebody could point it out to me.

The code


#include <iostream>

using namespace std;


struct Flags {
public:
    bool withCheese = false;
    bool withBiscuits = false;

};

class Event {
    std::string description = "The base class";
public:
    std::string id = "Base";

    Event() = default;

    ~Event() = default;

    Event(Event const &event) = default;

    Event &operator=(Event &&) = default;

    virtual void go(Flags &flags) {};

    virtual void printDescription() {
        std::cout << description << std::endl;
    }
};


class EatCheese : public Event {
    std::string description = "We eat cheese now";
public:
    using Event::Event;
    std::string id = "EatCheese";

    void go(Flags &flags) override {
        flags.withCheese = true;
        flags.withBiscuits = false;
    };
};

class EatBiscuits : public Event {
    std::string description = "We eat biscuits now";
public:
    using Event::Event;
    std::string id = "EatBiscuits";

    void go(Flags &flags) override {
        flags.withCheese = false;
        flags.withBiscuits = true;
    };
};


class Runner {
public:
    EatCheese eventA;
    EatBiscuits eventB;
    Event *current_event = &eventA; // The problematic line: this still points to Base, not eventA.

    Flags flags;

    Event* step() {
        cout << "description: ";
        current_event->printDescription();
        current_event->go(flags);


        if (current_event->id == "EatCheese") {
            cout << "We have EatCheese"<<endl;
            current_event = &eventB;
        }

        else if (current_event->id == "EatBiscuits") {
            cout << "We have EatBiscuits"<<endl;
            current_event = &eventA;
        }
        cout << endl;

        return current_event;
    }

};


int main() {
    Runner runner;
    cout << "current_event_id: " << runner.current_event->id << ", with biscuits: " << runner.flags.withBiscuits << ", with Cheese: " << runner.flags.withCheese << endl;
    runner.step();
    cout << "current_event_id: " << runner.current_event->id<< ", with biscuits: " << runner.flags.withBiscuits << ", with Cheese: " << runner.flags.withCheese << endl;
    runner.step();
    cout << "current_event_id: " << runner.current_event->id<< ", with biscuits: " << runner.flags.withBiscuits << ", with Cheese: " << runner.flags.withCheese << endl;
    return 0;
};

This will output

current_event_id: Base, with biscuits: 0, with Cheese: 0
description: The base class

current_event_id: Base, with biscuits: 0, with Cheese: 1
description: The base class

current_event_id: Base, with biscuits: 0, with Cheese: 1

Where I expect it to output:

current_event_id: EatCheese, with biscuits: 0, with Cheese: 1
description: We eat cheese now

current_event_id: EatBiscuits, with biscuits: 1, with Cheese: 0
description: We eat biscuits now

current_event_id: EatCheese, with biscuits: 1, with Cheese: 0```

You are referring to non-virtual members in your main function.

struct Base { int myNumber = 0; };

struct Child : Base { int myNumber = 10 };

both classes have a myNumber member but since Child is a derviative of Base it has actually 2 myNumber of which the one from the base class is hidden.

If you do

Child obj;
std::cout << obj.myNumber;

it would print 10, because your expression refers to Child::myNumber . However, if you make it a pointer or reference of your base class

Child obj;
Base* ptr = &obj;
Base& ref = obj;
std::cout << ptr->myNumber << ref.myNumber;

both will print 0 because the object is now treated as type Base . The member variable in question usually hides the base member, but since our variable has a different type now, the access path to that variable is different aswell and Base::myNumber is the symbol we're referring to now.

So create virtual getter functions:

class Event {
    std::string description = "The base class";
public:
    std::string id = "Base";

    virtual std::string const& getDescription() const;
    virtual std::string const& getId() const;
    // ...
};

and override them in your child class.

Also, for further reading I recommend: Why does an overridden function in the derived class hide other overloads of the base class?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM