从抽象基类实现纯虚函数:重写说明符有什么意义吗?

时间:2016-09-07 12:49:26

标签: c++

背景

我只是偶然发现override specifier的一个用例,就我所知,它似乎是多余的,也没有任何特定的语义含义,但也许我错过了一些东西,因此这个问题。在继续之前,我应该指出我已经尝试在SO上找到答案了,但是我得到的最接近的是以下主题,并没有真正回答我的问题(也许有人可以指出问答&答案实际上已经回答了我的问题。)

问题

考虑以下抽象类:

struct Abstract {
  virtual ~Abstract() {};
  virtual void foo() = 0;
};

在直接从override派生的非抽象类中实现foo()时是否有任何理由使用Abstract说明符(如下面的DerivedB)?即,当派生类已经需要foo()的实现是非抽象的(而不是真正覆盖任何东西)时?

/* "common" derived class implementation, in my personal experience
   (include virtual keyword for semantics) */
struct DerivedA : public Abstract {
  virtual void foo() { std::cout << "A foo" << std::endl; }
};

/* is there any reason for having the override specifier here? */
struct DerivedB : public Abstract {
  virtual void foo() override { std::cout << "B foo" << std::endl; }
};

4 个答案:

答案 0 :(得分:8)

我不是override的忠实粉丝,但是,假设它是一般有用的东西,那么,是的,将override放在虚拟函数上覆盖纯虚函数很有用。考虑这个相当人为的例子:

struct Base {
    virtual void f() = 0;
};

struct Derived : Base {
    virtual void f();
    virtual void f(int);
};

现在假设Base的维护者(甚至可能是你未来的自我)改变Base看起来像这样:

struct Base {
    virtual void f(int) = 0;
};

现在Derived的行为已经悄然改变了。使用override编译器将报告错误。

答案 1 :(得分:3)

从技术上讲,这两个版本在语法上都是正确合法的。 override说明符主要用于防止意外行为。编译器遇到标记为override的成员函数时将立即抛出错误,而该成员函数实际上并未覆盖虚函数。如果由于某种原因您更改了虚基类功能的签名,则可能会发生这种情况。考虑这个例子:

class Abstract {
    virtual void foo() { ...}
}; 

class Derived : public Abstract {
    void foo() override { ... }
};

现在,如果Abstract::foo的签名发生了变化,那就让我们说

class Abstract {
    virtual void foo(int bar) { ...}
}; 

编译器会在Derived::foo处抛出一个错误,因为它不再覆盖Abstract的函数,如果没有override限定符,它将不会。这有助于您更好地维护代码。但是,在您的特定情况下(即使用纯虚拟声明),也会抛出错误。所以使用override主要被认为是“良好做法”,我猜。有关该主题的更多信息:http://en.cppreference.com/w/cpp/language/override

答案 2 :(得分:2)

如果纯虚函数和编译不是真的。无论如何你都会得到一个错误(除了皮特的例子中)

但是如果您收到类似&#34; 您的功能未覆盖任何内容的错误&#34;错误消息可能更具可读性。与之后相比,&#34; 无法实例化抽象类&#34;

另一个优点是在阅读声明时,您知道这是来自基类的派生方法。

最好习惯用override声明所有重写的方法。那么为什么要在这里有所作为并且风格不一致。

至于为什么最好将所有重写方法声明为override

想象你有

class A
{
    virtual void Foo();
};

class B: public A
{
    virtual void Foo() override;
};

然后您将Foo更改为const中的A功能。没有override这将编译但是当你调用A->foo()并且它是一个B对象B->foo()时将不会被调用而没有任何指示在那里发生了变化。使用override,您会收到错误。

答案 3 :(得分:1)

这里override的主要优点是鼓励可维护性。请考虑以下事项:

class Foo
{
public: 
    virtual void foo() = 0;
};
class Derived : public Foo
{
public:
    //....
    virtual void foo(double x) override
    {
         //This throws error
    }
};

如上所示,如果编译上述内容,编译器将抛出错误。会发生什么是编译器会抱怨函数没有相同的签名。如果没有override关键字,结果会有所不同。