【问题标题】:Why is 'virtual' optional for overridden methods in derived classes?为什么派生类中重写方法的“虚拟”是可选的?
【发布时间】:2011-02-27 04:23:49
【问题描述】:

当一个方法在类中声明为virtual时,它在派生类中的覆盖也自动被视为virtual,并且C++语言在这种情况下使这个关键字virtual是可选的:

class Base {
    virtual void f();
};
class Derived : public Base {
    void f(); // 'virtual' is optional but implied.
};

我的问题是:将virtual 设为可选的理由是什么?

我知道编译器并非绝对有必要被告知,但我认为如果编译器强制执行这样的约束,开发人员将受益。

例如,有时当我阅读其他人的代码时,我想知道一个方法是否是虚拟的,我必须追踪它的超类来确定这一点。一些编码标准 (Google) 要求将 virtual 关键字放在所有子类中。

【问题讨论】:

    标签: c++ virtual derived-class


    【解决方案1】:

    由于语言不能强制执行“好的”风格,C++ 通常甚至不会尝试。至少在 IMO 中,在任何情况下包括这样的冗余说明符是否都是好的样式是值得商榷的(就我个人而言,我讨厌他们在那里)。

    (至少部分)Google 的编码标准可能在某些情况下是有意义的,但就 C++ 的一般情况而言,充其量只能算是平庸的建议。在某种程度上,他们甚至承认——他们公开声明的其中一些只是为了适应他们的旧代码。他们没有直接承认的其他部分,并且(说实话)这个论点无论如何都不支持他们的某些标准(即,其中一些似乎缺乏真正的理由)。

    【讨论】:

    • 并不是语言“不能”,而是它违背了 C++ 哲学。编译器可以检测到各种各样的样式违规,包括派生类中是否重用了“虚拟”。
    • @Michael:也许我说得不好,但我的意图是说它一般不能强制执行良好的风格,所以它经常不尝试,即使它显然可以(强制执行一些人认为好的风格)。
    【解决方案2】:

    是的,在这种情况下让编译器强制执行虚拟会更好,我同意这是为了向后兼容而维护的设计错误。

    但是,如果没有它,有一个技巧是不可能的:

    class NonVirtualBase {
      void func() {};
    };
    
    class VirtualBase {
      virtual void func() = 0;
    };
    
    template<typename VirtualChoice>
    class CompileTimeVirtualityChoice : public VirtualChoice {
      void func() {}
    };
    

    通过以上我们可以选择是否需要 func 的虚拟性:

    CompileTimeVirtualityChoice<VirtualBase> -- func is virtual
    CompileTimeVirtualityChoice<NonVirtualBase> -- func is not virtual
    

    ...但同意,这对于寻求函数的虚拟性的成本来说是一个小好处,而我自己总是尝试在任何适用的地方输入 virtual。

    【讨论】:

    • 认为您想从 NonVirtualBase 中删除 = 0 :)
    【解决方案3】:

    我同意设计的弱点。 我还认为,如果两个不同的事物有不同的语法,那将是真的很好:

    1. 声明一个虚函数。 IE。在派生类中可能被覆盖的函数。 (这个东西其实是在vtable中增加了一个新的函数入口。)
    2. 覆盖派生类中的虚函数。

    在重写函数时使用当前的 C++ 规则 - 很容易搞砸事情。如果您输入错误的函数名称(或在其参数列表中输入错误) - 那么您实际上是使用 (1) 而不是 (2)。

    而且您没有错误/警告。只需在运行时获得惊喜。

    【讨论】:

    • 同意,我认为这源于 Bjarne Stroustrup 和委员会希望拥有尽可能少的一组关键字......因此将相同的关键字重用于不同的事物。同样,我真的不喜欢在阴影发生时不发出警告:/
    【解决方案4】:

    这是一个好问题,我当然同意,如果派生类中的方法已在基类中声明为 virtual,则在派生类中重新声明 virtual 方法是一种很好的风格。虽然有一些语言在语言中建立了风格(例如 Google Go,在某种程度上还有 Python),但 C++ 并不是其中一种语言。虽然编译器当然有可能检测到派生类没有为基类中声明为“虚拟”的东西重用关键字“虚拟”(或者,更重要的是,派生类声明了一个与基类,并且它没有在基类中声明为虚拟的),事实上,许多编译器都有设置在发生这种情况时发出警告(甚至错误消息)。但是,在这个阶段,在语言中制定这样的要求是不切实际的,因为存在太多不那么严格的代码。此外,开发人员始终可以选择比语言更严格,并且可以调高编译器警告级别。

    【讨论】:

      【解决方案5】:

      作为相关说明,在 C++0x 中,您可以选择通过新的属性语法强制显式地覆盖。

      struct Base {
        virtual void Virtual();
        void NonVirtual();
      };
      
      struct Derived [[base_check]] : Base {
        //void Virtual(); //Error; didn't specify that you were overriding
        void Virtual [[override]](); //Not an error
        //void NonVirtual [[override]](); //Error; not virtual in Base
        //virtual void SomeRandomFunction [[override]](); //Error, doesn't exist in Base
      };
      

      您还可以通过[[hiding]] 属性指定何时隐藏成员。它使您的代码更加冗长,但它可以在编译时捕获许多烦人的错误,例如,如果您使用 void Vritual() 而不是 void Virtual() 并最终在您打算覆盖现有函数时引入了一个全新的函数。

      【讨论】:

        猜你喜欢
        • 2016-03-31
        • 2020-10-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-05-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多