C ++函数不接受具体实现

时间:2012-04-02 08:17:18

标签: c++ templates abstract-class

我正在尝试实现类型安全的事件总线。我坚持使用EventBus::subscribe函数,因为它不接受我的具体事件处理程序。在早期版本中,我将AbstractEventHandler仅作为抽象类实现,而不是模板。我对该实现没有任何问题。这就是为什么我认为实际问题是抽象模板。

以下代码是我的实现的精简版本。第一个块由事件总线的“骨架”及其所需的类组成,而第二个块显示事件,事件处理程序和主要的实际实现。

enum包含所有可用的不同事件。抽象事件是所有具体事件的基础。事件处理程序是一个抽象模板,其中事件作为模板类以确保类型安全。事件总线负责将所有已发布的事件分发给其各自的处理程序。

enum EVENT_TYPE 
{
    ON_EVENT_1,
    ON_EVENT_2
};

class AbstractEvent 
{
public:
    AbstractEvent() {};
    virtual ~AbstractEvent() {};

    virtual EVENT_TYPE type() = 0;
};

template<class T>
class AbstractEventHandler 
{
public:
    AbstractEventHandler() {};
    virtual ~AbstractEventHandler() {};

    virtual void on_event(T *event) = 0;
};

class EventBus 
{
public:
    EventBus() {};
    virtual ~EventBus() {};

    void subscribe(EVENT_TYPE type, 
                    AbstractEventHandler<AbstractEvent> *eventHandler) {
        // Add handler to vector for further use
    }

    void publish(AbstractEvent *event) {
        // send event to each handler in respective vector
    }
};

下面是我的具体事件和事件处理程序以及main()

class ConcreteEvent : public AbstractEvent 
{
public:
    ConcreteEvent() {};
    virtual ~ConcreteEvent() {};

    EVENT_TYPE type() {
        return ON_EVENT_1;
    };
};

class ConcreteEventHandler : public AbstractEventHandler<ConcreteEvent> 
{
public:
    ConcreteEventHandler() {}
    virtual ~ConcreteEventHandler() {};

    void on_event(ConcreteEvent *event) {
        // Do something
    };
};

int main() 
{
    EventBus *eventBus = new EventBus();

    ConcreteEventHandler handler = ConcreteEventHandler();

    // This failes!
    eventBus->subscribe(ON_EVENT_1, &handler);
}

编译器返回错误,指出调用

没有匹配函数
EventBus::subscribe(EVENT_TYPE, ConcreteEventHandler*) 

并且唯一的候选人是

void EventBus::subscribe(EVENT_TYPE, AbstractEventHandler<AbstractEvent>*) 

如何实现我的EventBus :: subscribe方法来接受抽象类的具体实现?

更新:解决方案

我已将EventBus::subscribe的方法说明更改为以下内容,现在效果很好:

template<typename T>
void subscribe(EVENT_TYPE type, AbstractEventHandler<T> *eventHandler) {

}

谢谢,罗汉,你的提示!他们帮助我找到了这个解决方案。

3 个答案:

答案 0 :(得分:5)

原因是,ConcreteEventHandlerAbstractEventHandler<ConcreteEvent>的子类,而不是AbstractEventHandler<AbstractEvent>

这可能看起来令人惊讶,但AbstractEventHandler<ConcreteEvent>不能是AbstractEventHandler<AbstractEvent>的子类,即使ConcreteEventAbstractEvent的子类。

原因是,使用模板,您可以根据需要进行模板化并不能保证类型安全。我们来看一个例子。让我们回顾一下基类Animal和子类CatDog的标准范例。假设我们有一份动物名单:

std::list<Animals>* animals;

和一系列猫:

std::list<Cat> cats;

以下内容不是有效的演员:

animals = &cats;

原因是,因为,如果我要这样做,

animals->add(new Dog("Ben"));

我实际上会将Dog添加到Cat列表中。 cats.last()这里实际上会返回Dog。因此,在这种情况下,您实际上是在Dog列表中添加Cat。我已经看过足够的Looney Tunes剧集,知道这不是一个好主意:

cats.last().meow();

以上情况绝对不正确,因为我们都知道Dog只能bowbow()

修改

要回答你的问题,我建议你这样做;让ConcreteEventHandler继承AbstractEventHandler<AbstractEvent>,并在代码中,无论您使用ConcreteEvent的哪个位置,都可以使用dynamic_caseAbstractEvent转换为ConcreteEvent }。这将使用运行时内省,这可能会影响性能(我也见过很多人反对使用动态转换),但是你将能够成功执行有效的数据类型的上传。

答案 1 :(得分:1)

Rohan已经回答了为什么代码无法编译,但是我想提出另一种方法。

您可以通过 Eventhandler直接订阅EventGenerator 的方式实现它。这样,在生成和处理事件之间存在直接联系 然后,事件应该保持对其生成器的引用,以允许它访问订阅的处理程序,并且eventbus调用事件上的方法以使其自己处理。

这样,eventbus不知道事件处理程序,你甚至不需要eventtype枚举。
但是,您需要不同的事件生成器,这些事件生成器需要由不同的事件处理程序访问,而不是一个事件总线。每个eventhandler只能处理一个事件,因此如果需要更多事件,则应该聚合事件处理程序(通过委托或继承)。

答案 2 :(得分:-2)

您的AbstractEventHandler<T>课程应该继承AbstractEvent。这可能是你的意图,你只是忘了写它。

template<class T>
class AbstractEventHandler
    :public AbstractEvent
{
public:
    AbstractEventHandler() {};
    virtual ~AbstractEventHandler() {};

    virtual void on_event(T *event) = 0;
}