【问题标题】:Issues implementing double dispatch in C++在 C++ 中实现双重调度的问题
【发布时间】:2021-01-22 21:43:08
【问题描述】:

感谢您花时间查看我的问题。我正在尝试在 C++ 中实现双重调度,但它似乎没有采取。我想我可能缺少一些东西?我不相信对象切片是我使用指针的问题。我已经阅读了有关重载解决方案的问题,并且双重调度是一种解决方法。任何帮助表示赞赏。

这是我的代码

class ReactType;
class ConditionerBase
  {
  public:
    ConditionerBase(/* args */){};
    ~ConditionerBase(){};
    virtual void visit(ReactType* reactor);
  };

  class ReactType
  {
  public:
    ReactType(/* args */){};
    ~ReactType(){};
    virtual void accept(ConditionerBase *conditioner);
  };
  
  class IdealReactType: public ReactType
  {
  public:
    IdealReactType(/* args */){};
    ~IdealReactType(){};
    virtual void accept(ConditionerBase *conditioner);
  };

  class Conditioner : public ConditionerBase
  {
  public:
    Conditioner(/* args */){};
    ~Conditioner(){};
    virtual void visit(ReactType* reactor){std::cout<<"Visited unknown reactor"<<std::endl;};
    virtual void visit(IdealReactType* ideal_reactor){std::cout<<"Visited ideal_reactor"<<std::endl;};
  };

  void ReactType::accept(ConditionerBase *conditioner){conditioner->visit(this);};
  void IdealReactType::accept(ConditionerBase *conditioner){conditioner->visit(this);};
  void ConditionerBase::visit(ReactType* reactor){std::cout<<"Base"<<std::endl;};

void doubleDispatchExample()
{

  std::vector<ReactType*> reactors;
  ReactType* unknown = new ReactType();
  IdealReactType* ideal_reactor = new IdealReactType();
  reactors.push_back(unknown);
  reactors.push_back(ideal_reactor);
  Conditioner conditioner;
  for (size_t i = 0; i < 2; i++)
  {
    reactors.at(i)->accept(&conditioner);
  }
  delete unknown;
  delete ideal_reactor;
}

我希望输出是

Visited unknown reactor
Visited ideal reactor

但我得到了

Base
Base

拨打doubleDispatchExample()

有人建议我将函数设为虚拟,这给了我

Visited unknown reactor
Visited unknown reactor

【问题讨论】:

  • 使函数 visitaccept 虚拟化。
  • @Eugene 使它们成为虚拟然后给我一个Visited unknown reactor Visited unknown reactor 的输出,这仍然不是我想要的。
  • @Eugene,这条评论让我找到了答案。谢谢!
  • 如果您在希望重载虚函数的函数上添加overload,这将大有帮助。
  • 上面的错字。 override,而不是 overload。接受建议。可以节省大量调试。

标签: c++ oop overloading


【解决方案1】:

我发现ConditionerBase 需要这两个类的重载函数,它给了我想要的输出。将其更改为以下内容可以得到我想要的输出。

class ReactType;
class IdealReactType;
class ConditionerBase
  {
  public:
    ConditionerBase(/* args */){};
    ~ConditionerBase(){};
    virtual void visit(ReactType* reactor);
    virtual void visit(IdealReactType* reactor);
  };

  class ReactType
  {
  public:
    ReactType(/* args */){};
    ~ReactType(){};
    virtual void accept(ConditionerBase *conditioner);
  };
  
  class IdealReactType: public ReactType
  {
  public:
    IdealReactType(/* args */){};
    ~IdealReactType(){};
    virtual void accept(ConditionerBase *conditioner);
  };

  class Conditioner : public ConditionerBase
  {
  public:
    Conditioner(/* args */){};
    ~Conditioner(){};
    virtual void visit(ReactType* reactor){std::cout<<"Visited unknown reactor"<<std::endl;};
    virtual void visit(IdealReactType* ideal_reactor){std::cout<<"Visited ideal_reactor"<<std::endl;};
  };

  void ReactType::accept(ConditionerBase *conditioner){conditioner->visit(this);};
  void IdealReactType::accept(ConditionerBase *conditioner){conditioner->visit(this);};
  void ConditionerBase::visit(ReactType* reactor){std::cout<<"Base Reactor"<<std::endl;};
  void ConditionerBase::visit(IdealReactType* reactor){std::cout<<"Base Ideal"<<std::endl;};

void doubleDispatchExample()
{

  std::vector<ReactType*> reactors;
  ReactType* unknown = new ReactType();
  IdealReactType* ideal_reactor = new IdealReactType();
  reactors.push_back(unknown);
  reactors.push_back(ideal_reactor);
  Conditioner conditioner;
  for (size_t i = 0; i < 2; i++)
  {
    reactors.at(i)->accept(&conditioner);
  }
  delete unknown;
  delete ideal_reactor;
}

【讨论】:

  • 从具体类派生通常是一个糟糕的设计。最好从一个公共抽象基派生 2 个具体类。
  • 此外,当您想对面向对象的访问者模式使用双重分派时,请考虑 C++17 标准库提供的通用编程替代方案:std::variant std::visit
  • @Eugene 你能举例说明派生 2 个具体类的样子吗?我有点困惑如何直接在这里应用。
  • @Eugene 你的意思是让一些功能纯粹是虚拟的,例如virtual void visit(ReactType* reactor)=0;?
  • 是的,“抽象基类”是指具有纯虚函数的类。具体类派生自它并覆盖这些功能。我并不是说基类永远不能包含非纯函数,而是在基类中实现所有函数并实例化该基类通常是一个坏主意。
猜你喜欢
  • 1970-01-01
  • 2016-06-06
  • 2021-12-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多