简体   繁体   中英

Wrong child class function being called due to signature difference

I have an event emitter that emits events and event handlers that handle events. I can extend the Event object to make different events like AlarmEvent , and the EventHandler object to make AlarmEventHandler . EventHandler has a function HandleEvent(Event &event) . This causes a problem for child classes that may have HandleEvent(AlarmEvent &event) methods. Obviously these are two different functions so there is no overriding taking place here. I need instead for HandleEvent to be overridden by the child class.

I fully understand the problem is that each EventHandler has a different HandleEvent signature so the EventEmitter will always handle events with the base EventHandler::HandleEvent . I thought by using Event &event as a paremeter to EventEmitter::Emit it would know what kind of Event it was dealing with and choose the correct method.

How can I have my EventEmitter call AlarmEventHandler::HandleEvent instead of the base method 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);
}

Use a dynamic_cast !

So your AlarmEventHandler could look like:

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

To do this though, you'll need Event to be a polymorphic class. So you make the destructor virtual :

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

See it run here: https://ideone.com/KMkLfq

You are not overriding HandleEvent() , you are overloading it.

You need a pointer or reference to be able to take advantage of dynamic dispatch to not have to cast.

Comments inline:

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

Compiled with

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

Output:

Handle alarm event 

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