C多态:我错过了什么?

我正在学习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的回答为您提供了一种替代方法,从长远来看,它应该更适合您.

上一篇:java – 多态性和向下转换问题


下一篇:java – 重载和重写方法中的多态性