我正在学习c并希望构建类似于C#事件的东西来处理嵌入式c项目中的中断.
到目前为止,我想出了一个几乎能满足我想要的解决方案.但是我需要一些关于多态性的帮助(?).以下代码片段是重现我的情况的最小示例:
#include <iostream>
struct Event
{ };
struct EventHandler
{
virtual void Esr (const Event& I) { }
};
struct EventSender
{
EventSender (EventHandler& Handler) : _Handler (Handler) { }
template <typename T>
void SendEvent (const T&) const
{
_Handler.Esr (T ());
}
EventHandler& _Handler;
};
struct SpecialEvent : public Event
{ };
struct MyHandler : public EventHandler
{
void Esr (const Event& I) override { std::cout << "Event" << std::endl; }
void Esr (const SpecialEvent& I) { std::cout << "SpecialEvent" << std::endl; }
};
int main()
{
MyHandler handler;
EventSender sender (handler);
/* Invoke directly */
handler.Esr (Event ());
handler.Esr (SpecialEvent ());
/* Invoke indirectly */
sender.SendEvent (Event ());
sender.SendEvent (SpecialEvent ()); // Expected cout msg: "SpecialEvent"
return 0;
}
预期的控制台输出:
Event
SpecialEvent
Event
SpecialEvent
实际控制台输出:
Event
SpecialEvent
Event
Event
这里的编译器/链接器我不知道什么?
解决方法:
MyHandler中有两个方法.其中一个覆盖基类方法
另一个没有.
一种解决方案是在基类中声明两个方法:
struct EventHandler
{
virtual void Esr (const Event& I) = 0;
virtual void Esr (const SpecialEvent& I) = 0;
};
这样,编译器可以使用参数的类型来解析EventHandler级别的方法.
如果您想避免要求所有派生类必须重载两个方法,您可以执行以下操作:
struct EventHandler
{
virtual void Esr (const Event& I) = 0;
virtual void Esr (const SpecialEvent& I)
{
// if not overridden, use the non-specialized event handler.
Esr(reinterpret_cast<const Event &>(I));
}
};
回答你的问题:
What does the compiler/linker here that I am not aware of?
在C中,方法调用在编译/链接时解析为1)对特定代码块(方法体)的调用,或2)通过称为vtable的隐藏数据结构的间接调用.实际的vtable是在运行时确定的,但编译器必须决定表中的哪个条目用于调用. (Google vtable提供了有关它们是什么以及它们如何实现的更多信息.)
它必须将此决议基于它允许知道的内容.在这种情况下,基于通过其调用方法的指针或引用的类型.请注意,这不一定是实际对象的类型.
在您调用throgh处理程序的情况下,允许编译器知道在MyHandler中声明的两个方法,以便它可以选择您期望的方法,但是当调用通过发送方时,它必须找到在EventSender中声明的方法.在EventSender中只声明了一种方法.幸运的是,这个论点可以被强制转换成一个const事件&所以编译器能够使用该方法.因此它使用该方法的vtable条目.所以它找到了MyHandler的vtable [在运行时]并使用vtable条目
Esr (const Event& I)
这就是你最终采用错误方法的方法.
顺便说一句:我的回答是为了解释你所看到的内容,并为你提供解决当前问题的方法. Jerry Coffin的回答为您提供了一种替代方法,从长远来看,它应该更适合您.