Вызвана неправильная функция дочернего класса из-за разницы в сигнатурах - PullRequest
1 голос
/ 21 января 2020

У меня есть источник событий, который испускает события, и обработчики событий, которые обрабатывают события. Я могу расширить объект Event для создания различных событий, таких как AlarmEvent, и объект EventHandler для создания AlarmEventHandler. EventHandler имеет функцию HandleEvent(Event &event). Это вызывает проблему для дочерних классов, которые могут иметь HandleEvent(AlarmEvent &event) методы. Очевидно, что это две разные функции, поэтому здесь нет переопределения. Вместо этого мне нужно, чтобы HandleEvent был переопределен дочерним классом.

Я полностью понимаю, проблема в том, что каждый EventHandler имеет свою собственную подпись HandleEvent, поэтому EventEmitter всегда будет обрабатывать события с база EventHandler::HandleEvent. Я подумал, что используя Event &event в качестве пареметра для EventEmitter::Emit, он узнает, с каким Event он имеет дело, и выберет правильный метод.

Как мне сделать мой EventEmitter вызов AlarmEventHandler::HandleEvent вместо базового метода EventHandler::HandleEvent?

// Example program
#include <iostream>
#include <string>
#include <vector>

// event types
class Event {};
class AlarmEvent : public Event {};

// event handler
class EventHandler {
    public:
        virtual void HandleEvent(Event event);
};
void EventHandler::HandleEvent(Event event){
    std::cout << "Handle event " << std::endl;   
}

// alarm event handler
class AlarmEventHandler : public EventHandler {
    public:
        void HandleEvent(AlarmEvent event);  
};
void AlarmEventHandler::HandleEvent(AlarmEvent event){
    std::cout << "Handle alarm event " << std::endl;   
}

// event emitter
class Emitter {
    public:  
        std::vector<EventHandler*> handlers;
        void Emit(Event &event);
};
void Emitter::Emit(Event &event){
    for(size_t i = 0; i < this->handlers.size(); i++){
        this->handlers[i]->HandleEvent(event);   
    }
}

int main()
{
    AlarmEventHandler handler;
    AlarmEvent event;
    Emitter emitter;
    emitter.handlers.push_back(&handler);
    // problem:
    // Handle event printed instead of Handle alarm event
    emitter.Emit(event);
}

Ответы [ 2 ]

3 голосов
/ 21 января 2020

Вы не переопределяете HandleEvent(), вы перегружаете его.

Вам нужен указатель или ссылка, чтобы иметь возможность использовать динамическую c диспетчеризацию, чтобы не вызывать ее.

Комментарии:

#include <iostream>
#include <string>
#include <vector>

// event types
class Event {
    public:
        virtual ~Event();                       // make base destructor virtual
};

Event::~Event() {}

class AlarmEvent : public Event {};

// event handler
class EventHandler {
    public:
        virtual void HandleEvent(Event& event);
        virtual ~EventHandler();                 // virtual base destructor
};

EventHandler::~EventHandler() {}

void EventHandler::HandleEvent(Event& event){
    std::cout << "Handle event " << std::endl;   
}

// alarm event handler
class AlarmEventHandler : public EventHandler {
    public:
        //void HandleEvent(AlarmEvent& event);  // overload, does not override
        void HandleEvent(Event& event);
};

void AlarmEventHandler::HandleEvent(Event& event){
    std::cout << "Handle alarm event " << std::endl;   
}

// event emitter
class Emitter {
    public:
        Emitter() : handlers() {}
        std::vector<EventHandler*> handlers;
        void Emit(Event &event);
};

void Emitter::Emit(Event& event) {
    for(size_t i = 0; i < this->handlers.size(); i++){
        this->handlers[i]->HandleEvent(event);   
    }
}

int main() {
    AlarmEventHandler handler;
    AlarmEvent event;
    Emitter emitter;
    emitter.handlers.push_back(&handler);
    emitter.Emit(event);
}

Скомпилировано с

g++ -std=c++98 -O3 -Wall -Wextra -Weffc++ -pedantic -pedantic-errors

Вывод:

Handle alarm event 
3 голосов
/ 21 января 2020

Используйте dynamic_cast!

Чтобы ваш AlarmEventHandler мог выглядеть следующим образом:

// alarm event handler
class AlarmEventHandler : public EventHandler {
    public:
        void HandleEvent(const Event &event); // Our polymorphic override
        void HandleEvent(AlarmEvent event);  // Our custom Alarm logic
};

void AlarmEventHandler::HandleEvent(const Event &event){
    try {
        HandleEvent(dynamic_cast<const AlarmEvent&>(event));
    } catch(const std::exception& e) {
        std::cerr << "I can't handle things that aren't AlarmEvents!" << std::endl;
    }
}

void AlarmEventHandler::HandleEvent(const AlarmEvent &event){
    std::cout << "Handle alarm event " << std::endl;   
}

Для этого вам понадобится Event быть полиморфом c класс. Итак, вы создаете деструктор virtual:

class Event { 
    public: virtual ~Event() {} //Need this so Event is a polymorphic class
};

Смотрите его здесь: https://ideone.com/KMkLfq

...