【问题标题】:Implementing pure virtual function from abstract base class: does override specifier have any meaning?从抽象基类实现纯虚函数:重写说明符有什么意义吗?
【发布时间】:2017-01-15 04:11:03
【问题描述】:

背景

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

问题

考虑以下抽象类:

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

在直接从Abstract 派生的非抽象类中实现foo() 时,是否有任何理由使用override 说明符(如下面的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; }
};

【问题讨论】:

  • 1) 它有助于确定派生的方法。 2)可能它可以帮助编译器进行一些优化它与java中的相同(@override)
  • 这是一个不错的选择,以防您更改了基础中的签名但忘记更新派生。
  • 从技术上讲,您在子类中也不需要virtual,但您可以使用它。 override 更好,因为它更清楚地传达了意图。
  • @KrzysztofBargieł:“帮助”如何?编译器肯定知道该函数是一个覆盖。 override 是为人类服务的。
  • @peppe 有理由实现覆盖。如果您更改基类中的签名(在非纯虚函数上),everythign 仍然可以编译,并且如果没有覆盖,您不会收到任何错误。

标签: c++


【解决方案1】:

我不是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,编译器会报错。

【讨论】:

    【解决方案2】:

    从技术上讲,这两个版本在语法上都是正确且合法的。 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

    【讨论】:

      【解决方案3】:

      在纯虚函数和编译的情况下不是真的。无论如何你都会得到一个错误(除了Pete的例子)

      但是,如果您收到类似“您的函数没有覆盖任何东西”这样的错误,则错误消息可能比后来的“无法实例化抽象类”更易读 p>

      另一个好处是在阅读声明时您知道这是从基类派生的方法。

      另外,习惯于用override 声明所有被覆盖的方法也是一个好习惯。那么为什么要在这里有所作为并且风格不一致。

      至于为什么最好将所有被覆盖的方法声明为override

      假设你有

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

      然后你将Foo 更改为A 中的const 函数。如果没有override,它将编译,但是当您调用A-&gt;foo() 并且它是一个B 对象时,B-&gt;foo() 将不会在没有任何指示的情况下被调用。使用 override 时会出现错误。

      【讨论】:

        【解决方案4】:

        override 的主要优点是鼓励可维护性。考虑如下:

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

        正如您在上面看到的,如果您编译上述内容,编译器会抛出错误。会发生什么情况是编译器会抱怨该函数没有相同的签名。如果没有 override 关键字,结果会有所不同。

        【讨论】:

          猜你喜欢
          • 2014-10-21
          • 2013-04-20
          • 1970-01-01
          • 1970-01-01
          • 2013-06-29
          • 2011-04-27
          • 2012-10-06
          • 2019-04-20
          • 1970-01-01
          相关资源
          最近更新 更多