【问题标题】:Counting objects without RTTI计数没有 RTTI 的对象
【发布时间】:2012-06-01 08:09:19
【问题描述】:

我遇到了一个让我觉得很愚蠢的问题。在一个爱好项目中,我有一个指向接口类的指针的 std::list,它指向所述接口的各种具体实现。

例如,假设我有以下内容:

class Seafood ...
class Fishstick : public Seafood ...
class Squid : public Seafood ...
...
std::list<Seafood*> buffet;

现在我的自助餐里摆满了不同的海鲜,我想数一数我有多少鱼条,看看是否需要从厨房订购更多。

如果没有 RTTI 或其一些迂回的实现,我将如何做到这一点?我读过一些文章声称如果您发现自己想要使用 RTTI,那么您正在以错误的方式接近 OOP 和/或您的解决方案应该重新设计。是否有一些模式或其他解决方案可以解决这个问题?我敢肯定之前已经出现过很多次了。

我在想很明显这是某种虚函数,但如果不构建一个俗气的 RTTI 版本,或者对接口中的后代的一些知识(CountIfFishstick / IsFishstick / Is),我无法弄清楚如何做到这一点(类型))。

edit:想到的另一件事是保留一份fishsticks 列表、一份squid 列表等。但这肯定会破坏接口/实现的整个目的。

【问题讨论】:

  • 正如你所说,这里有一点代码味道:要么你关心特定类型(你似乎关心),要么你不关心(使用接口)。请注意,多态性不是能够将它们一起存储在容器中,而是能够通过固定接口使用派生类型(例如在调用函数时),因此您可能希望将它们分开(出于计数目的)和尚未使用界面或其他用途。
  • 如果您绝对必须检查变量的类型以确定其性质,bool isFishstick = !!dynamic_cast&lt;Fishstick *&gt;(value); 应该可以工作。也就是说,您应该寻找解决此问题的其他方法。
  • 害怕RTTI之类的东西其实很傻。有气味并不表示设计不良,它表明可能存在不良设计。虽然多态性是关于同等对待事物,但有时有必要(例如在计算具体类型时)深入研究继承的更高层次。这样做而不是其他一些令人费解的事情通常是最简单和最好的方法。

标签: c++ oop design-patterns polymorphism


【解决方案1】:

复合模式怎么样?自助餐真的是海鲜系列的集合。 FishStick 和 Squid 是 Composite 模式中的“组件”,它们将保持其项目数。所以当 Buffet 是 list 时,它可以遍历和调用 Composites 上的计数。

【讨论】:

    【解决方案2】:

    大概你在基类中有一个name 函数来返回项目的名称,这样你就可以把它展示给厨房了。只需使用它来索引项目计数地图。

    通常,您可以提供一个函数,该函数返回每个类的任何唯一标识符。

    【讨论】:

      【解决方案3】:

      如果您使用的是 C++11,您可以执行以下操作:

      int num_fish_sticks = std::count_if(buffer.begin(), buffet.end(),
          [](const SeaFood* sf) {sf->is_fish_stick()});
      

      你需要在 SeaFood 中声明一个纯虚函数:

      virtual bool is_fish_stick() const = 0;
      

      并在子类中相应地实现它。

      编辑:当然,如果您有太多子类,这可能会很麻烦。在这种情况下,您最好只使用 RTTI:

      int num_fish_sticks = std::count_if(buffer.begin(), buffet.end(),
          [](const SeaFood* sf) {typeid(*sf) == typeid(Fishstick)});
      

      【讨论】:

      • 这比使用 RTTI 还要糟糕。
      • @AndreasMagnusson 好吧,OP 想要一个不使用 RTTI 的解决方案。我将使用 RTTI 更新我的答案以包含更通用的方式。
      • 是的,对于一个好的解决方案,请参阅我的和 Crazy Eddies 的回复。访问者模式是一种在不更改原始类的情况下将新的虚函数“添加”到类层次结构的方法。
      【解决方案4】:

      我认为您的问题比您想象的要基本得多:至少在您提出的情况下,似乎根本没有太多(任何?)理由在这里使用继承。

      当您的对象具有不同的行为时,继承很有用,但在这种情况下,它们似乎都具有基本相同的行为——事实上,几乎没有任何行为(除非 count "装死”)。

      如果我要为自助餐建模,我可能使用容器。热桌上的每个托盘都是一个容器,里面装着一些东西。如果需要,您可以将桌子建模为托盘容器,并且(可能)将房间建模为桌子容器。

      每个托盘都有一个“食品”(或您喜欢的任何名称),您通常有一个名称和数量。唯一的行为可能是处理食物何时“供应”的计时器,并基于该计时器何时需要移除任何剩余物。您可能拥有更多功能,例如将食物分为仅作为全餐一部分提供的食物,与仅购买沙拉吧使用权的食物分开。

      【讨论】:

        【解决方案5】:

        Visitor pattern 是您正在寻找的。还有一个特殊版本的访问者,称为 Acyclic visitor,它使用 RTTI 来解决原始访问者的一些问题,所以 RTTI 并不总是错误的,但它可能导致可怕的代码,除非你真的知道你在做什么...

        【讨论】:

          【解决方案6】:

          您可能想要访问者模式的一些变体。有很多,很难说你想要哪个。我可能会推荐Modern C++ Design 并查看 Alexendrescu 的实现。否则,谷歌“访问者模式”,你会得到 1000 公里的链接来阅读。

          【讨论】:

          • 我可以在 4 分钟内接受这个作为答案,但是可以。这种模式会很好地工作。我将扩展我的问题以回答您的建议。
          猜你喜欢
          • 1970-01-01
          • 2010-09-15
          • 1970-01-01
          • 2022-12-14
          • 2016-11-05
          • 2016-04-02
          • 2021-01-28
          • 2019-06-30
          • 2013-08-03
          相关资源
          最近更新 更多