【问题标题】:C++ classes hierarchy design choiceC++ 类层次结构设计选择
【发布时间】:2016-04-22 17:42:55
【问题描述】:

在我的模拟中,我可以通过三种方式感知不同的对象:可以看到和/或听到和/或闻到对象。例如,可以看到、听到和闻到动物。地上的一块肉看得见摸得着,听不见,只能看到墙。然后我有不同的传感器来收集这些信息 - EyeSensorEarSensorNoseSensor

前状态:简版gist.github.com link

在我开始实现 NoseSensor 之前,我在一个类中拥有所有三个功能,每个对象都继承了 - CanBeSensed,因为尽管类不同,但它们都需要相同的 getDistanceMethod(),如果对象实现了任何 CanBeSensed 功能,它需要一个 senseMask - 标记是否可以听到/看到/闻到对象并且我不想使用虚拟继承。我牺牲了在这个类中为气味、声音、EyeInfo 设置数据成员,因为只能看到的对象不需要气味/声音信息。

然后将对象注册到相应的传感器中。

现在我注意到气味传感器和声音传感器是相同的,并且仅在循环内的一行中有所不同 - 一个调用 float getSound(),另一个调用 float getSmell() 上的 CanBeSensed* 对象。当我创建这两个传感器之一时,我知道它需要调用什么,但我不知道如何在没有条件的情况下选择那条线,它位于一个紧密循环和一个虚函数内。

所以我决定使用getDistanceMethod() 的基类的虚拟继承为这三个功能创建一个基类。

但是现在因为这个方法我不得不把我的 SensorBase 类变成一个模板类

virtual void sense(std::unordered_map<IdInt, CanBeSensed*>& objectsToSense) = 0;

,这意味着我还需要将SensorySubSystem 类(管理范围内的传感器和对象)作为模板。这意味着我的所有子系统(如 VisionSubSystem、HearingSubSystem 和 SmellSubSystem)都继承自模板类,它破坏了我的 SensorySystem 类,该类通过指向SensorySubSystemstd::vector<SensorySubSystem*> subSystems; 的指针向量管理所有 SensorySubSystems

请您提出一些解决方案,以解决如何重组这个问题或如何让编译器在编译时决定(或至少每次调用决定一次//每次创建对象一次)在 Hearing/Smell 中调用什么方法Sensor秒。

【问题讨论】:

  • 未来您是否可能在系统中添加更多类型的传感器?
  • @MartinBroadhurst 好吧,也许吧。您是否建议将我所有的子系统都变成成员变量而不是将它们保存在一个数组中?
  • 我在想,如果只有 3 种感官,并且您不太可能再添加,那么在您的基础 CanBeSensed 类中拥有 3 个虚函数并拥有不能添加的对象就没有错被一种特定的感觉感知到通过什么都不返回来实现它。
  • 是的,您的SensorySubsystem 也可以将传感器作为成员变量。
  • 您是否担心不能拥有指向模板类对象的指针向量?如果您从非模板类派生模板类,然后使向量成为指向该非模板基类的对象的指针,则可以。

标签: c++ c++11 inheritance multiple-inheritance


【解决方案1】:

看你的原始设计我有几个cmets:

  • hierarchy.cpp 中的类设计在我看来还不错。
  • 除非距离是特定于感官信息的东西,否则 getDistance() 看起来不像属于此类的方法。它可以移动到 Vec2d 类或辅助函数 (calculatePositon(vec2d, vec2d))。我不明白为什么 getDistance() 是虚拟的,如果它做的事情与计算给定位置和对象位置之间的距离不同,那么它应该被重命名。
  • CanBeSensed 类听起来更像是一个属性,应该重命名为 e.g.可感知对象。

关于您的新方法:
继承应该主要用于表达概念(is-a-relations),而不是共享代码。如果您想重用算法,请考虑编写算法类或函数(优先使用组合而不是继承)。

总之,我建议保持你原来的类设计清理它一点如上所述。您可以将虚拟函数 canBeSmelled/canBeHeard/canBeSeen 添加到 CanBeSensed。
或者,您可以创建一个类层次结构:

  • 类对象{ getPosition(); }
  • 类 ObjectWithSmell : 虚拟对象
  • 类 ObjectWithSound : 虚拟对象
  • ...

但是你必须处理虚拟继承而没有任何明显的好处。
共享的计算代码可以进入算法类或函数。

【讨论】:

  • 关于 getDistance。问题是对象的当前中心是在其中计算的。实际距离很容易计算。 pastebin.com/Qgtv2HEJ 。嗯,那么 getPos 我猜是代码重复,哈哈。感谢您的 cmets,我认为从第一天起我的设计就很糟糕。我在这里是因为它。您能否详细说明如何在我的案例中应用组合?我偶然发现继承不是为了代码重用和组合的事情,但我不知道如何在这里应用它。用一个具体的例子来推动会有很大帮助。
  • 问题是,我会采用虚拟继承的新方法,然后在 CanBeHeard/Smelled 中,我会将 getSound/getSmell 重命名为相同的名称,并且我的耳鼻传感器将是模板化的家伙和一个第 50 行的函数调用将自行解决。但问题是我需要模板化的子系统,它会破坏指向子系统基类的 SensorySystem 指针数组。
  • 不要把事情搞得太复杂,我不认为你的原创设计不好。您担心的代码重复是什么?您可以在重新设计后发布您的代码吗?
  • 事实上我还没有重新设计。虚拟继承将迫使我使用模板,而模板将迫使我为 SensorySubSystem 创建另一个未模板化的基类(因为 SensorySystem 中的向量),它将提供模板化版本将重新定义并用于调用其模板化版本的虚拟函数。功能。我认为虚拟功能已经太多了。可能是我没有得到什么,不知道。现在,我只是......将整个 Ear 类代码复制到 Nose 类中并使用旧设计。 i.imgur.com/YsV3BDs.png
  • 从您的示例开始,您似乎可以将大量代码从您的特定传感器类移动到 SensorBase。您不仅可以在基类中实现“sense”-Method(参见pastebin.com/03ekrcq7)。
猜你喜欢
  • 1970-01-01
  • 2013-11-21
  • 2011-05-07
  • 1970-01-01
  • 2014-12-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多