当前位置: 首页 > 知识库问答 >
问题:

是否建议显式地将覆盖函数设为虚拟?

燕钟展
2023-03-14

在C 11之前,当在派生类中重写虚拟函数时,建议将虚拟关键字也添加到派生类函数中,以明确其意图。

如今,这样的函数被标记为“override ”,这包含了必须有一个虚基类函数的概念。因此,我现在宁愿省略虚拟的:

class Derived: public Base {
public:
  void Overriden() override;
  // Instead of: virtual void Overriden() override;
};

然而,这会导致MSVC 2012中的IntelliSense错误:“覆盖”修饰符需要带有显式“虚拟”关键字的虚函数声明

显然编译器编译了类,但错误让我想到了它。是否仍有正当理由添加虚拟关键字?

共有3个答案

钱华晖
2023-03-14

是的,当重写基类行为时,您应该更喜欢使用override而不是virtual。因为这可能会导致错误

这怎么可能导致错误呢?这里有一个例子,

#include <iostream>
using namespace std;

class Base {
 public:
    virtual void foo() {
        std::cout << "BASE foo" << std::endl;
    }
    void bar() {
        std::cout << "BASE bar" << std::endl;
    }
};


class A : public Base{
 public:
    void foo() override {
        std::cout << "A foo" << std::endl;
    }
    virtual void bar() { 
        std::cout << "A bar" << std::endl;
    }
};

class B : public A {
 public:
    void bar() override { 
        std::cout << "B bar" << std::endl;
    }
};


int main(int argc, char *argv[])
{
    B b;
    A *a = &b;
    Base *base = &b;
    base->foo();
    a->foo();
    base->bar();
    a->bar();
    return 0;
}

输出将是

A foo
A foo
BASE bar
B bar

foo() 被正确覆盖,但 bar 没有被正确覆盖,并且在某些情况下会变得隐藏。对 bar() 的调用不会调用相同的方法,即使底层对象相同

如果我们强制自己在重写时始终使用<code>override</code>,而<code>virtual</code>仅用于定义新的虚拟函数,那么当我们尝试<code>void bar()override{}</cod>时,编译器会抱怨<code>错误:“void A::bar()”标记为“override”,但不会重写</code>

这也正是Autosar规范定义以下规则的原因

规则A10-3-1(必需、实现、自动化)虚拟函数声明应包含三个说明符中的一个:(1)虚拟、(2)覆盖、(3)最终。

理由:指定这三个说明符中的多个以及虚函数声明是多余的,并且是潜在的错误来源。

董翰墨
2023-03-14

我认为这更多的是品味问题:-)

我更喜欢只在定义后面写覆盖,这意味着该函数已经是虚拟的。根据定义,程序员是懒惰的,所以保持源代码简短:-)

我们在编码指南中添加了一条规则,重写是必须的,在正常的变更过程中或者重构实际的类时,虚代码应该从旧代码中移除。

但这只是我们的解决方案,没有技术原因!

黄高爽
2023-03-14

正如在over关键字的留档中报告的那样,它的意思是手头的函数必须覆盖一个虚函数:

在方法声明中,override指定函数必须重写基类方法。

这是一种强制执行(即让编译器强制执行)这种要求的方法。当然,如果基类的函数不是虚拟的,代码就不会编译。因此,正如您所指出的,添加虚拟是多余的。

我认为,在C11之前加上它也不是好建议。考虑以下代码片段:

#include <iostream>

using namespace std;

class A{
    public:
    void f(){
        cout << "A" << endl;
    }
};

class B : public A{
    public:
    virtual void f(){
        cout << "B" << endl;
    };
};

class C : public B{
    public:
    void f(){
        cout << "C" << endl;
    };
};

int main( int argc, char* argv[] ) 
{
    C c;
    A& aref = c;
    aref.f();
    B& bref = c;
    bref.f();
}

其输出显然是“A”后跟“C”。正如您所看到的,在< code > C类中添加< code>virtual将没有任何效果,而< code > B类中的< code>virtual起着关键作用。遵循将< code>virtual添加到< code > C类的惯例会使这一点更难一眼看出。

 类似资料:
  • 我想知道为什么我们不应该重写非虚拟函数?

  • 链接的问题不一样 - 甚至没有提到 编辑:新的副本列表包含一个合法的副本,我在搜索中没有找到。 在问这个问题之前,我不知道是否在派生类成员中使用的选择对某些人来说将是一个有争议的问题。 我刚刚遇到了一些如下所示的源代码: 我不确定如何解释这个,甚至在读完这个之后。 这里的< code>override是指< code>virtual吗?< code>void B::method()未被标记为虚函数

  • 问题内容: 你能 声明这样 的函数吗? 然后像这样 重新声明 它… 这样可以覆盖函数吗? 可以吗 问题答案: 编辑 解决此答案未直接解决原始问题的评论。如果您是通过Google搜索到达的,请从此处开始 有一个名为override_function的函数可以实际使用。但是,由于此函数是Advanced PHP Debugger扩展的一部分,因此很难为生产使用提供参数。因此,我要说“否”,不可能以原始

  • 我的@SpringBootApplication注释存在于包中。 根据本文,使用@SpringBootApplication注释等同于使用@Configuration、@EnableAutoConfiguration和@ComponentScan及其默认属性:- https://docs.spring.io/spring-boot/docs/2.0.x/reference/html/using-b

  • 问题内容: 是否有可能覆盖 全局 功能,从而在一定程度上影响 全局 功能? 据我所知,该函数在包装NodeJS脚本的函数中作为参数提供: 有什么方法可以修改功能吗? 这可能只会影响脚本所在的脚本。 我们如何在流程级别进行修改? 问题答案:

  • 问题内容: 这个: 在html中调用,例如: 向div元素添加一个字符串。正是我想要的。但是,这: 这样称呼: 似乎正在覆盖我的整个html文件。即,当我在Firefox中运行它时,它仅显示字符串(即页面的全部内容),但页面似乎仍在加载(FF的加载图标仍在进行动画显示,显然是无限的)。 首先,这将仅在本地,离线使用,作为呈现数据的快速便捷方式(使用html + js和网络浏览器而不是纯文本文件)。