【问题标题】:Avoiding dynamic_cast/RTTI避免 dynamic_cast/RTTI
【发布时间】:2009-02-24 22:59:47
【问题描述】:

我最近正在为一个业余项目(cpp-markdown library,出于好奇)编写一段 C++ 代码,并遇到了一个我想提出一些意见的编码问题。

cpp-markdown 有一个名为Token 的基类,它有许多子类。其中两个主要子类是Container(它包含其他Tokens 的集合)和TextHolder(用作Tokens 的基类,当然包含文本)。

大部分处理是通过虚函数处理的,但其中一些最好在单个函数中处理。为此,我最终使用dynamic_cast 将指针从Token* 向下转换为其子类之一,因此我可以调用特定于子类及其子类的函数。强制转换不可能失败,因为代码能够通过虚函数(例如isUnmatchedOpenMarker)判断何时需要这种东西。

我可以看到其他两种方法来处理这个问题:

  1. 创建 所有 我想调用的函数作为 Token 的虚函数,并为每个子类留下一个空的主体,除了需要的子类处理它们,或者...

  2. Token 中创建一个虚函数,当在某些子类型上调用它时,它将返回指向this 的正确类型的指针,如果在其他任何东西上调用它,则返回一个空指针。基本上是我已经在那里使用的虚拟功能系统的扩展。

对我来说,第二种方法似乎比现有方法和第一种方法都好。但我想知道其他有经验的 C++ 开发人员对此的看法。或者我是否过于担心琐事。 :-)

【问题讨论】:

    标签: c++ casting


    【解决方案1】:

    #1 污染了不需要它的对象的类命名空间和 vtable。好的,当你有一些通常会被实现的方法时,但是当只需要一个派生类时就很丑了。

    #2 只是dynamic_cast<> 穿着波点连衣裙和口红。不会使客户端代码变得更简单,并且会混淆整个层次结构,要求基类和每个派生类都对每个其他派生类半感知。

    只需使用dynamic_cast<>。这就是它的用途。

    【讨论】:

      【解决方案2】:

      如果你想变聪明,你还可以构建一个double dispatch 模式,它是visitor pattern 的三分之二。

      • 创建一个基类 TokenVisitor 包含空的虚拟 visit(SpecificToken*) 方法。
      • 向 Token 添加一个虚拟 accept(TokenVisitor*) 方法,该方法在传递的 TokenVisitor 上调用正确类型的方法。
      • 从 TokenVisitor 派生您需要以不同方式对所有令牌执行的各种操作。

      对于完整的访问者模式,对树结构很有用,让默认的 accept 方法迭代每个调用 token->accept(this); 的子级。

      【讨论】:

        【解决方案3】:

        如果您知道转换不会无效,那么只需使用 static_cast。

        【讨论】:

        • 同意,如果你已经在用虚函数检查类型,那么 dynamic_cast 就没用了。您的其他两个解决方案都只是粉饰您的层次结构已被破坏。如果您要进行 hack,至少使用更快的 hack。
        • 如果派生类使用虚拟继承从 Token 继承,static_cast 将不起作用。在这种情况下,dynamic_cast 不会没用。
        【解决方案4】:

        为什么要避免使用dynamic_cast?它是否会导致您的应用程序出现不可接受的瓶颈?如果不是,现在可能不值得对代码做任何事情。

        如果您可以在特定情况下以一定的安全性换取一点速度,那么您应该可以static_cast;但是,这巩固了您的假设,即您知道对象的类型,并且演员阵容没有机会变坏。如果以后您的假设变得错误,您的代码中可能会出现一些神秘的崩溃错误。回到我最初的问题,你真的确定这里的权衡值得吗?

        至于您列出的选项: 第一个听起来不像是一个解决方案,而是我希望在凌晨 3 点编写代码时看到的最后一分钟的黑客攻击。向类层次结构的基础逐渐渗透的功能是 OOP 新手遇到的最常见的反模式之一。不要这样做。

        对于第二个选项,您列出的任何类似的选项实际上只是重新实现 dynamic_cast - 如果您在一个只有垃圾编译器可用的平台上(我听说过有关 Gamecube 的编译器占用系统可用的四分之一的故事带有 RTTI 信息的 RAM)这可能是值得的,但很可能您只是在浪费时间。你真的确定这是一件值得你关心的事情吗?

        【讨论】:

        • 不,我不确定这是否值得关注。这就是我问的原因。 :-)
        • 如上所述:如果使用虚拟继承,static_cast 会失败。
        【解决方案5】:

        防止 dynamic_cast 真正干净的方法是将正确类型的指针放在正确的位置。抽象应该负责其余的工作。

        恕我直言,dynamic_cast 有此声誉的原因是,每次您在类层次结构中向下添加另一个子类型时,它的性能都会下降一点。如果层次结构中有 4-5 个类,则无需担心。

        【讨论】:

          【解决方案6】:

          有趣的东西。分词器中的dynamic_cast 表示您实际上希望将 Token 拆分为基于文本流中的当前位置和 Token 中的逻辑创建 Token 的东西。每个令牌要么是独立的,要么必须像上面一样创建一个令牌才能正确处理文本。

          这样你就可以提取通用的东西,并且仍然可以根据令牌的层次结构进行解析。您甚至可以在不使用 dynamic_cast 的情况下使整个解析器由数据驱动。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2011-12-02
            • 1970-01-01
            • 1970-01-01
            • 2011-05-24
            • 1970-01-01
            • 2013-08-03
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多