【问题标题】:using virtual function vs dynamic_cast使用虚函数 vs dynamic_cast
【发布时间】:2013-12-30 16:53:40
【问题描述】:

不使用虚函数,是否可以使用类似的东西:

void BaseClass::functionName () {  // BaseClass already has virtual functions
    // some LONG code true for all derived classes of BaseClass
    // ...
    if (typeid (*this) == typeid (DerivedClass1))
        // use functions of DerivedClass1 on dynamic_cast<DerivedClass1*>(this)
    else if (typeid (*this) == typeid (DerivedClass2))
        // use functions of DerivedClass2 on dynamic_cast<DerivedClass2*>(this)

    // some LONG code true for all derived classes of BaseClass
    // ...
 }

只是我觉得像上面那样使用虚函数不是一个好主意,因为它只是专门用于派生类的一小部分。用于所有派生类的长代码将需要一遍又一遍地用于所有派生类(为此建议使用辅助函数)。当然,我已经测试了我的方法并且它有效(并且我认为性能没有损失),但我想知道这是否是有问题的做法。 如果在函数中多次使用 if-else-if 部分怎么办?

如果所有派生类的公共代码相对较短,那么最好使用虚函数,对吗?

【问题讨论】:

  • 使用适当的语言进行标记将有助于您的问题被正确的用户发现。
  • 这取决于你对“错误”的定义。 C++ 多态性的全部意义在于避免这样的代码。基类必须知道其所有派生类也很奇怪。所以听起来你所追求的是template method pattern。如果您不习惯将任意行为放入各个类中,您可以随时应用 visitor pattern 或类似的东西。
  • 另外,关于性能。如果这是在某个时间关键的内部循环中,一堆顺序类型查找将比虚函数调用慢很多
  • 虚拟函数是专门发明的,所以你不需要做那样的事情。这是为了避免在 70 年代/80 年代初期常见的巨大的 if-else/switch 语句。像使用 goto 一样谨慎使用 RTTI。它现在可能用于一小部分,但未来呢?维护人员会看到您在做什么,并复制相同的样式但功能非常强大。突然之间,这将在所有地方使用,而我们在你不知不觉中,你又回到了大量的 if-else。

标签: c++ function virtual dynamic-cast


【解决方案1】:

为什么不这样做:

void BaseClass::functionName () {
    // some LONG code true for all derived classes of BaseClass
    // ...

    this->some_protected_virtual_member_function();

    // some LONG code true for all derived classes of BaseClass
    // ...
 }

因此,公共部分不会重复,并且该行为仍然可以轻松地在您的子类中进行扩展,而无需向您的父类添加另一个 if

【讨论】:

  • 你的意思是protected虚拟成员函数吗?
  • 注意:上述函数可以是private,没有问题;你可以重写 private virtual 函数,只是不能显式调用父类的实现。
  • @MatthieuM。然而,有些人会争辩说,即使您的子类也不知道私有函数的语义,而受保护的函数恰好适合这个角色:)
  • @Drax:这是一个相当个人的解释,不是吗?我宁愿坚持语言开辟的可能性,因为它们相当明确:privatevirtual 函数可能会被覆盖但不会被调用。
  • @MatthieuM。 protected virtual 函数可能会被覆盖和调用,是的,我想你的观点是有道理的 :)
【解决方案2】:

除非类具有虚函数,否则您的代码将根本无法工作。 C++ 仅提供有限的反射: typeid(DerivedClass1)==typeid(DerivedClass2) 如果没有虚函数。上面的代码也可能比简单地访问一个虚函数要慢:你会为每种类型获得一个新的分支,而不是一个常量时间指针查找。

然而,上述代码最大的问题是它失去了多态性和封装性。使用代码必须知道或 DerivedClass1 和 DerivedClass2 需要做什么。它需要了解 DerivedClass1 和 DerivedClass2 内部的结构。而且,所有的代码都堆在一个地方,使得这个函数可能有数百行。

【讨论】:

    【解决方案3】:

    我认为您在这里寻找模板方法模式:只需使用您现有的非虚拟函数并让它只为具体类之间不同的一小部分代码调用虚拟函数。它还有看起来更漂亮的优点。

    void BaseClass::functionName () {
        // some LONG code true for all derived classes of BaseClass
        // ...
    
        functionName_impl();  // Will be virtual (private or protected) and overriden in each child class to do the right work.
    
        // some LONG code true for all derived classes of BaseClass
        // ...
     }
    

    【讨论】:

      【解决方案4】:

      这是Template Method Pattern的退化案例:

      class Base {
      public:
          void templated() {
              // do some stuff
      
              this->hook1();
      
              // other stuff
      
              if (/*cond*/) { this->hook2(); }
      
              size_t acc = 0;
              for (Stuff const& s: /*...*/) { acc += this->hook3(s); }
      
              // other stuff
          }
      
      private:
          virtual void hook1() {}
          virtual void hook2() {}
          virtual size_t hook3(Stuff const&) { return 0; }
      }; // class Base
      

      然后Derived 类可以自定义钩子的行为。

      一个警告:本质上这是非常严格的,因为templated 方法不是virtual;这是这种模式的优点和问题,这很好,因为如果您需要更改 templated 方法,那么它会在一个地方定义,如果提供的钩子不足以自定义行为,这很烦人.

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多