事件处理:当接收者期望派生类时,从基类发送事件

Event handling: Sending events from base classes when the receiver expects derived classes

本文关键字:基类 事件 接收者 期望 派生 事件处理      更新时间:2023-10-16

这段代码将问题提炼到本质:

基础设施类:

struct EventReceiverBase {
    virtual ~EventReceiverBase() { }
};
template<typename T>
struct EventReceiver : public virtual EventReceiverBase {
    virtual void receiveEvent(T* pSender) = 0;
};
struct EventSender {
    EventReceiverBase* pReceiver;
    template<typename T>
    void sendEvent(T* pSender) {
        EventReceiver<T>* pCastedReceiver =
            dynamic_cast<EventReceiver<T>*>(pReceiver);
        // HERE IS THE PROBLEM
        // pCastedReceiver is null. T is BaseSender. The pointer
        // being casted is really of type EventReceiver<DerivedSender>*,
        // but it tries to cast to EventReceiver<BaseSender>*, and that
        // fails.
        pCastedReceiver->receiveEvent(pSender);
    }
};

用户类:

struct BaseSender : public virtual EventSender {
    void f() {
        sendEvent(this);
    }
};
struct DerivedSender : public BaseSender { };
struct MyClass : public virtual EventReceiver<DerivedSender> {
    void receiveEvent(DerivedSender* pSender) { }
};
int main() {
    MyClass my;
    DerivedSender derivedSender;
    derivedSender.pReceiver = &my;
    derivedSender.f();
}

我可以改写这个问题(没有双关语)来避免这个问题吗?我想保持用户类尽可能简单,同时暴露事件发送和接收功能尽可能接近这种方式。

例如,我可以通过让MyClass从EventReceiver也可以,但我真的希望避免这样做,因为这意味着在每个接收事件的类中都要做额外的工作。

编辑:可执行粘贴:http://liveworkspace.org/code/4bm6OU$13

您看到问题的原因与f函数在BaseSender中的位置有关:在下面的sendEvent调用中,this代表指向BaseSender的指针。实际上,

下面的调用
void f() {
    sendEvent(this);
}

是一个简短的写法:

void f() {
    sendEvent<BaseSender>(this);
}

移动fDerivedSender可以解决这个问题。

另一种选择是使BaseSender::f成为模板:

template <typename T>
void f() {
    sendEvent<T>((T*)this);
}

并像这样调用它:

derivedSender.f<DerivedSender>();

这看起来不是特别好,但在实际f很大的情况下,这可能是代码复制粘贴的一种解决方法。

另一个解决方案是使BaseSender成为模板,如下所示:

template<typename T>
struct BaseSender : public virtual EventSender {
    void f() {
        sendEvent((T*)this);
    }
};
struct DerivedSender : public BaseSender<DerivedSender> {
};

这不应该是个问题。pSender将指向原始对象,除非你做了一些非常奇怪的事情。

然而,你的MyClass中的receiveEvent应该接受一个BaseSender *对象,然后你必须将它强制转换为DerivedSender(或者更好的是,只使用在基类中定义的虚拟方法-这是你应该做的事情)