C++ 选择基于方法的参数的动态类型

C++ choose method based argument's dynamic type

问题

假设我有一些模拟器库,它从我那里获取一些对象(也称为事件处理程序)并通过调用它们的 handle_event(Event) 方法为这些对象生成事件。图书馆为我提供了以下 classes:

class Event {}; // Base class for all events
// All event classes are derived from `Event`
class SomeParticularEvent : public Event {}; // Some event class
class AnotherParticularEvent : public Event {}; // Some event class


// Object aka event handler. I should inherit this class and then give objects to the simulator
class AbstractEventHandler
{
public:
        virtual void handle_event(Event) = 0;
};

我想实现一个以不同方式处理不同事件的对象。我想出的第一个代码如下:

#include <iostream>

class MyObject : public AbstractEventHandler
{
public:
        void actual_handle_event(SomeParticularEvent)
        {
                std::cout << "`SomeParticularEvent` occurred\n";
        }

        void actual_handle_event(AnotherParticularEvent)
        {
                std::cout << "`AnotherParticularEvent` occurred\n";
        }

        void actual_handle_event(Event e)
        {
                std::cerr << "Unknown event type occurred\n";
        }

        virtual void handle_event(Event e) override
        {   
                actual_handle_event(e);
        }   
};

与我的预期相反,MyObject::handle_event(Event) 将始终调用 MyObject::actual_handle_event(Event),而不管 e.

的动态类型如何

我的问题是:实施 MyObject 的正确方法是什么(最好是能够轻松添加新的事件类型)?


所有代码一起

#include <iostream>


class Event {}; // Base class for all events
// All event classes are derived from `Event`
class SomeParticularEvent : public Event {}; // Some event class
class AnotherParticularEvent : public Event {}; // Some event class


class AbstractEventHandler
{
public:
        virtual void handle_event(Event) = 0;
};


class MyObject : public AbstractEventHandler
{
public:
        void actual_handle_event(SomeParticularEvent)
        {
                std::cout << "`SomeParticularEvent` occurred\n";
        }

        void actual_handle_event(AnotherParticularEvent)
        {
                std::cout << "`AnotherParticularEvent` occurred\n";
        }

        void actual_handle_event(Event e)
        {
                std::cerr << "Unknown event type occurred\n";
        }

        virtual void handle_event(Event e) override
        {
                actual_handle_event(e);
        }
};


int main()
{
        MyObject o{};
        o.handle_event(SomeParticularEvent{}); // Prints "Unknown event type occurred"
}

附加问题

是否也可以创建一个从 MyObject 派生的 class 并实现 额外的 事件处理程序(不覆盖旧的,但添加对新的支持事件)而不重写派生的 class?

中的 handle_event 方法

在您的方法中 handle_event() 发生对象切片。您应该传递 (const) 引用以使继承正常工作,或者传递指针。

但是,当从 handle_event() 内部调度到 actual_handle_event() 时,这对您的情况没有帮助。

一个蛮力解决方案是通过尝试 dynamic_casts 来显式调度。您需要在派生 类 中覆盖 handle_event() 以扩展它。它看起来也不那么漂亮(表明设计问题)。

在这些情况下通常所做的是,所讨论的对象 (Event) 将提供一个接口来查询类型,例如被覆盖的抽象 type() getter。

根据问题中的要求和@ypnos answer 的评论中讨论的内容,我相信您希望或需要在此处实现访问者模式。

一种可能的实现如下所示(基于关于访问者模式的维基百科文章:https://en.wikipedia.org/wiki/Visitor_pattern#C++_example

#include <iostream>

// Forward declare all Event classes
class Event;
class SomeParticularEvent;
class AnotherParticularEvent;

class AbstractEventHandler
{
public:
        virtual void handle_event(const Event&) = 0;
        virtual void handle_event(const SomeParticularEvent&) = 0;
        virtual void handle_event(const AnotherParticularEvent&) = 0;
};

class Event {
public:
    // virtual function for accepting the "visitor"
    virtual void accept(AbstractEventHandler& handler) {
        handler.handle_event(*this);
    }
}; // Base class for all events
// All event classes are derived from `Event`
class SomeParticularEvent : public Event {
public:
    // Needs to be overriden to call `handle_event` with the correct type
    void accept(AbstractEventHandler& handler) override {
        handler.handle_event(*this);
    }
}; // Some event class
class AnotherParticularEvent : public Event {
public:
    void accept(AbstractEventHandler& handler) override {
        handler.handle_event(*this);
    }
}; // Some event class

class MyObject : public AbstractEventHandler
{
public:
        void handle_event(const SomeParticularEvent&) override
        {
                std::cout << "`SomeParticularEvent` occurred\n";
        }

        void handle_event(const AnotherParticularEvent&) override
        {
                std::cout << "`AnotherParticularEvent` occurred\n";
        }

        void handle_event(const Event& e) override
        {
                std::cerr << "Unknown event type occurred\n";
        }
};


int main()
{
        MyObject o{};
        SomeParticularEvent{}.accept(o);
        AnotherParticularEvent{}.accept(o);
}