Как я могу достичь полиморфизма в этом простом минимальном примере? - PullRequest
0 голосов
/ 06 января 2020

Проблема

Я пытаюсь достичь полиморфизма, используя типы подтипов Event, наследуемых от базового класса Event. Идея состоит в том, что всегда есть активное событие, обозначаемое указателем в Runner (Event* current_event). Когда вызывается метод Runner::step, вызывается метод Event::go, который переключает некоторые переключатели и изменяет текущее поведение программы. Затем указатель указывает на другое событие, пока Runner::step не будет вызван снова.

Проблема в том, что указатель всегда использует базовый класс Event версий переменных. Это может быть простым исправлением, но я был бы рад, если бы кто-то мог указать мне на это.

Код


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

Это выведет

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

Где я ожидаю, что это выведет:

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```

1 Ответ

1 голос
/ 06 января 2020

Вы имеете в виду не виртуальные члены в своей основной функции.

struct Base { int myNumber = 0; };

struct Child : Base { int myNumber = 10 };

оба класса имеют член myNumber, но, поскольку Child является деривацией Base, в действительности она имеет 2 myNumber из которых один из базового класса скрыт.

Если вы сделаете

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

, вы получите 10, потому что ваше выражение ссылается на Child::myNumber. Однако, если вы сделаете это указателем или ссылкой на ваш базовый класс

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

, оба будут печатать 0, потому что объект теперь обрабатывается как тип Base. Рассматриваемая переменная-член обычно скрывает базовый член, но поскольку наша переменная теперь имеет другой тип, путь доступа к этой переменной также отличается, и Base::myNumber - это символ, на который мы ссылаемся сейчас.

Поэтому создайте виртуальные функции получения:

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

и переопределите их в своем дочернем классе.

Кроме того, для дальнейшего чтения я рекомендую: Почему переопределяется функции в производном классе скрывают другие перегрузки базового класса?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...