虚拟?重写?或两者?C++

Dan*_*iel 80 c++ c++11

在过去的几周里,有些东西正在困扰我的大脑虚拟和覆盖.我已经了解到,当你使用虚函数进行继承时,你必须添加virtual以让编译器知道搜索正确的函数.之后我还了解到在c ++ 11中有一个新关键字 - 覆盖.现在我有点困惑; 我是否需要在程序中同时使用虚拟和覆盖关键字,或者最好只使用其中一个?

为了解释自己 - 我的意思的代码示例:

class Base
{
public:
    virtual void print() const = 0;
    virtual void printthat() const = 0;
    virtual void printit() const = 0;
};

class inhert : public Base
{
public:
    // only virtual keyword for overriding.
    virtual void print() const {}

    // only override keyword for overriding.
    void printthat() const override {}

    // using both virtual and override keywords for overriding.
    virtual void printit() const override {}
};
Run Code Online (Sandbox Code Playgroud)

什么是最好的方法?

Che*_*Alf 130

当你覆盖一个函数时,你在技术上不需要写virtualoverride.

原始基类声明需要使用关键字virtual将其标记为虚拟.

在派生类中,函数是通过将¹same类型作为基类函数来虚拟的.

但是,override当预期的覆盖在技术上不是覆盖时,可以通过产生编译错误来帮助避免错误.例如,函数类型与基类函数不完全相同.或者基类的维护会更改该函数的类型,例如添加默认参数.

同样virtual,通过确保函数在其他派生类中仍然是虚拟的,派生类中的关键字可以使这样的错误更加微妙.

所以一般的建议是,

  • 使用virtual基类的函数声明.
    这在技术上是必要的.

  • 使用override(仅)派生类的覆盖.
    这有助于维护.

例:

struct Base { virtual void foo() {} };
struct Derived: Base { void foo() override {} };
Run Code Online (Sandbox Code Playgroud)

注意:
¹C++支持协变原始指针和原始参考结果.对于协方差,覆盖的类型不完全相同.它只是一个兼容的类型.

  • @iambr 不,覆盖不会改变*正确*程序的行为,而是对*不正确*程序大声喊叫。这绝对不是无用的。 (24认同)
  • 覆盖是可选的,正如这个答案所说,有助于检测编码错误 (17认同)
  • 来自cppreference(http://en.cppreference.com/w/cpp/language/virtual):"如果某个成员函数vf在类Base中声明为virtual,而某些类Derived是直接或间接派生的,来自Base,有一个成员函数的声明,具有相同的名称参数类型列表(但不是返回类型)cv-qualifiers ref-qualifiers然后Derived类中的这个函数也是虚拟的(无论关键字virtual是否在其中使用)声明)并覆盖Base :: vf(无论是否在其声明中使用了单词覆盖)." (14认同)
  • 所以总而言之`override`是没用的 (2认同)
  • @23W 该措辞通常允许不同的返回类型,以便适应协变指针和引用返回类型。其他一些段落可能不允许其他变体,并且在任何情况下都不会被编译器接受。协变指针和引用之所以有效,是因为无论类型如何,大小都是相同的,并且协变是安全的(相关:里氏替换原理,LSP)。 (2认同)

bob*_*obo 58

根据C++ 核心指南 C.128,每个虚拟函数声明应准确virtual指定、override或之一final

  • virtual:对于基类中函数的“第一次”出现
  • overridevirtual:对于从某个基类派生的类中重写该函数,提供virtual相同(或协变)签名的函数
  • final:用于将覆盖标记为不可覆盖。也就是说,具有final虚函数重写的类的派生类不能重写该虚函数重写。
struct A {
    virtual void go() { puts("A"); }
};
struct B : A {
    // Overrides A::go(). No need to specify `virtual` explicitly,
    // it already implicitly is virtual and overrideable
    void go() override { puts("B"); }
};
struct C : B {
    void go() final { puts("C"); }
    //virtual void go() override final { puts("C"); } would still compile,
    // but it is considered, for lack of a better description, "extra"
};
struct D : C {
    // Would produce error C3248 : 'C::go' : function declared as 'final' cannot be overridden by 'D::go'
    //void go() override { puts("D"); }
};
Run Code Online (Sandbox Code Playgroud)