【问题标题】:Are non-pure virtual functions with parameters bad practice?带有参数的非纯虚函数是不好的做法吗?
【发布时间】:2008-11-03 23:56:21
【问题描述】:

我有一个带有可选虚函数的基类

class Base {
    virtual void OnlyImplementThisSometimes(int x) {}
};

当我编译这个时,我收到一个关于未使用的参数 x 的警告。还有其他一些我应该实现虚拟功能的方法吗?我已经这样重写了:

class Base {
    virtual void OnlyImplementThisSometimes(int x) 
    {
        x = 0;
    }
};

我还有一个问题,如果我不小心,我制作的子类可能会实现错误的功能,然后因为重载而我没有注意到:例如

class Derived : public Base {
    void OnlyImplementThisSometimes(int x, int y) { // some code }
};

Derived d;
Base *b = dynamic_cast<Base *>(&d);
b->OnlyImplementThisSometimes(x); // calls the method in the base class

之所以调用基类方法,是因为我使用“int y”参数实现了派生函数,但对此没有任何警告。这些只是 C++ 中的常见缺陷还是我误解了虚函数?

【问题讨论】:

    标签: c++ polymorphism


    【解决方案1】:

    忽略设计问题,您可以通过省略变量名称来绕过编译器警告未使用变量的问题,例如:

    virtual void OnlyImplementThisSometimes(int ) { }
    

    在尝试覆盖虚函数时错误地实现了错误的方法签名只是您在 C++ 中需要小心的事情。像 C# 这样的语言使用 'override' 关键字来解决这个问题。

    【讨论】:

    • 另一种方法(效果相同)是注释掉内联变量名,例如:int /*x*/。如果变量名称是描述性的(应该是),这将有助于读者。
    • @Nick:我坚信强类型安全,其中类型描述意图(并防止参数混淆)。如果你(大部分)这样编码,参数名称并不重要。
    • 可以编辑最后一段以反映 C++ 中的 override 关键字。
    【解决方案2】:

    我们将宏 _unused 定义为:

    #define _unused(x) ((void)x)
    

    然后定义函数为:

    virtual void OnlyImplementThisSometimes(int x) { _unused( x );}
    

    这不仅使编译器不会抱怨,而且让任何维护代码的人都清楚你没有忘记 x —— 你是有意忽略它。

    【讨论】:

    • 如果您使用 Qt 编程,您可以使用与此建议解决方案相同的 Q_UNUSED 宏。
    【解决方案3】:

    为什么要在基类中定义呢?如果基类不打算使用该方法,那么只需将其定义为派生类中的虚拟方法即可。

    或者默认实现可能会抛出异常

    【讨论】:

      【解决方案4】:

      如果您提供了一个虚函数的默认实现,它应该是所有不覆盖该函数的派生类的正确实现。如果您不能提供 正确 实现,那么我的建议是制作一个纯虚函数并将其留给派生类来提供实现。不允许调用方法的派生类可以抛出异常,保证不被误用。

      【讨论】:

      • 如果你能想到一个通常正确但并不总是正确的实现,那么你可以提供一个纯虚函数的实现,并且在正常情况下派生类只是调用它。
      【解决方案5】:

      除了简单地省略变量名之外,在许多编译器中,您可以告诉编译器,您知道它是未使用的,并且通过这样做来关闭

      int func(int x)
      {
         (void) x;
      }
      

      【讨论】:

      • 严格来说,这并没有告诉编译器它是未使用的并且 SHUTUP。它使用它。
      【解决方案6】:

      这在我的代码中有些常见。例如,我有专为单线程操作和多线程设计的类。有很多常见的套路和数据。我把所有这些都放在了基类中(它也有几个纯虚拟)。

      我在基类中实现了两个空虚函数:Init() 和 Cleanup()。单线程派生类不隐含它们,但多线程派生类可以。

      我有一个工厂函数创建适当的派生类,然后返回一个指针。客户端代码只知道基类类型,它调用 Init() 和 Cleanup()。这两种情况都是正确的。

      当然,关于如何做到这一点,可能还有其他好的建议,但是这个习惯用法适用于我的很多代码。

      【讨论】:

      • 我相信,在派生类中重写纯虚函数并让它什么都不做会比在基类中执行此操作而不在派生类中重写更清晰、更容易使用代码。它不太可能被滥用,因为该类的用户必须考虑它。
      【解决方案7】:

      这不是一个坏习惯,而且它是一种常见的习惯用法,用于指定类的可选实现部分。

      目前我将它用于用户输入系统,因为该类的用户实现每一个方法都会很乏味,即使他很可能不会使用它。

      class mouse_listener{
      public:
          virtual ~mouse_listener() {}
      
          virtual void button_down(mouse_button a_Button) {}
          virtual void button_up(mouse_button a_Button) {}
          virtual void scroll_wheel(mouse_scroll a_Scroll) {}
          virtual void mouse_move_abs(math::point a_Position) {}
          virtual void mouse_move_rel(math::point a_Position) {}
      };
      

      【讨论】:

        【解决方案8】:

        顺便说一句,如果你知道你的基类,就永远不需要做动态的up-casts,即从派生到基类。

        Base *b = &d;
        

        也可以,dynamic_cast&lt;&gt; 应该在你向下转换时使用,即从基础到派生:

        if((Derived *d = dynamic_cast<Derived *>(b)) != 0)
        {
          // use d
        }
        

        (当然在向下转换的情况下,static_cast&lt;&gt; 通常也可以。)

        【讨论】:

        • 我可以想到至少需要动态向上转换的一种情况:如果您从模板参数派生并且需要根据基本类型调用特定函数。就我而言,它是这样的: if(fixture *f = dynamic_cast(this)){f->set_up();}
        • 由于框架的设置方式,我当时不知道测试用例是否源自fixture:测试用例本身源自模板参数。跨度>
        【解决方案9】:

        试试这个:

        class Base {
            virtual void OnlyImplementThisSometimes(int x) = 0;
        };
        

        我已经有一段时间没有做过类似的事情了,但我相信这就是你声明虚函数的方式。

        正如其他人所说,变量名称在这样的函数声明中是可选的。

        【讨论】:

          【解决方案10】:

          最简单的答案如下所示:

          class Base {
              virtual void OnlyImplementThisSometimes(int x) { x;}
          };
          

          对绝对不执行任何操作的变量的简单引用将删除所有警告(无论如何来自最高级别的 VC++)。

          【讨论】:

          • 首先,这仍然在 GCC 中使用 -Wall 发出警告(“语句无效”)。其次,这不是最简单的答案——省略参数名是。
          猜你喜欢
          • 1970-01-01
          • 2018-08-05
          • 2016-01-01
          • 1970-01-01
          • 2021-03-20
          • 1970-01-01
          • 1970-01-01
          • 2014-11-01
          • 2017-05-06
          相关资源
          最近更新 更多