派生类中函数的 C++“虚拟”关键字。有必要吗?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4895294/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 16:52:31  来源:igfitidea点击:

C++ "virtual" keyword for functions in derived classes. Is it necessary?

c++overridingvirtual-functions

提问by Anarki

With the struct definition given below...

使用下面给出的结构定义......

struct A {
    virtual void hello() = 0;
};

Approach #1:

方法#1:

struct B : public A {
    virtual void hello() { ... }
};

Approach #2:

方法#2:

struct B : public A {
    void hello() { ... }
};

Is there any difference between these two ways to override the hello function?

这两种覆盖 hello 函数的方法有什么区别吗?

采纳答案by James McNellis

They are exactly the same. There is no difference between them other than that the first approach requires more typing and is potentially clearer.

它们完全一样。除了第一种方法需要更多的输入并且可能更清晰之外,它们之间没有区别。

回答by Clifford

The 'virtualness' of a function is propagated implicitly, however at least one compiler I use will generate a warning if the virtualkeyword is not used explicitly, so you may want to use it if only to keep the compiler quiet.

函数的“虚拟性”是隐式传播的,但是如果virtual未显式使用关键字,我使用的至少一个编译器将生成警告,因此您可能希望使用它,如果只是为了保持编译器安静。

From a purely stylistic point-of-view, including the virtualkeyword clearly 'advertises' the fact to the user that the function is virtual. This will be important to anyone further sub-classing B without having to check A's definition. For deep class hierarchies, this becomes especially important.

从纯粹的风格角度来看,包括virtual关键字清楚地向用户“宣传”了该功能是虚拟的这一事实。这对于任何进一步对 B 进行子类化而无需检查 A 的定义的人来说都很重要。对于较深的类层次结构,这变得尤为重要。

回答by R Sahu

The virtualkeyword is not necessary in the derived class. Here's the supporting documentation, from the C++ Draft Standard (N3337) (emphasis mine):

virtual关键字是不是在派生类中必要的。这是支持文档,来自 C++ 草案标准 (N3337)(重点是我的):

10.3 Virtual functions

2 If a virtual member function vfis declared in a class Baseand in a class Derived, derived directly or indirectly from Base, a member function vfwith the same name, parameter-type-list (8.3.5), cv-qualification, and ref-qualifier (or absence of same) as Base::vfis declared, then Derived::vfis also virtual (whether or not it is so declared) and it overrides Base::vf.

10.3 虚函数

2如果虚拟成员函数vf是在类中声明Base和一个类Derived,直接或间接地来源于Base,成员函数vf具有相同的名称,参数类型列表(8.3.5),CV-资格,和ref-限定符(或不存在相同)作为Base::vf声明,那么Derived::vf也是虚拟的(无论是否如此声明)并且它覆盖Base::vf.

回答by Colin D Bennett

No, the virtualkeyword on derived classes' virtual function overrides is not required. But it is worth mentioning a related pitfall: a failure to override a virtual function.

不,virtual派生类的虚函数覆盖上的关键字不是必需的。但值得一提的是一个相关的陷阱:无法覆盖虚函数。

The failure to overrideoccurs if you intend to override a virtual function in a derived class, but make an error in the signature so that it declares a new and different virtual function. This function may be an overloadof the base class function, or it might differ in name. Whether or not you use the virtualkeyword in the derived class function declaration, the compiler would not be able to tell that you intended to override a function from a base class.

未能覆盖,如果你打算在派生类中重写一个虚函数,但做出一个错误的签名,以便它声明了一个新的和不同的虚函数发生。此函数可能是基类函数的重载,也可能名称不同。无论您是否virtual在派生类函数声明中使用关键字,编译器都无法判断您打算覆盖基类中的函数。

This pitfall is, however, thankfully addressed by the C++11 explicit overridelanguage feature, which allows the source code to clearly specify that a member function is intended to override a base class function:

然而,幸运的是,C++11显式覆盖语言功能解决了这个陷阱,它允许源代码明确指定成员函数旨在覆盖基类函数:

struct Base {
    virtual void some_func(float);
};

struct Derived : Base {
    virtual void some_func(int) override; // ill-formed - doesn't override a base class method
};

The compiler will issue a compile-time error and the programming error will be immediately obvious (perhaps the function in Derived should have taken a floatas the argument).

编译器将发出编译时错误,并且编程错误将立即显现(也许 Derived 中的函数应该将 afloat作为参数)。

Refer to WP:C++11.

参考WP:C++11

回答by Sujay Ghosh

Adding the "virtual" keyword is good practice as it improves readability , but it is not necessary. Functions declared virtual in the base class, and having the same signature in the derived classes are considered "virtual" by default.

添加“virtual”关键字是一种很好的做法,因为它可以提高可读性,但这不是必需的。默认情况下,在基类中声明为虚拟且在派生类中具有相同签名的函数被视为“虚拟”。

回答by harper

There is no difference for the compiler, when you write the virtualin the derived class or omit it.

当你virtual在派生类中编写或省略它时,编译器没有区别。

But you need to look at the base class to get this information. Therfore I would recommend to add the virtualkeyword also in the derived class, if you want to show to the human that this function is virtual.

但是您需要查看基类以获取此信息。因此,我建议virtual在派生类中也添加关键字,如果你想向人类展示这个函数是虚拟的。

回答by lorro

There's a considerable difference when you have templates and start taking base class(es) as template parameter(s):

当您拥有模板并开始将基类作为模板参数时,会有很大的不同:

struct None {};

template<typename... Interfaces>
struct B : public Interfaces
{
    void hello() { ... }
};

struct A {
    virtual void hello() = 0;
};

template<typename... Interfaces>
void t_hello(const B<Interfaces...>& b) // different code generated for each set of interfaces (a vtable-based clever compiler might reduce this to 2); both t_hello and b.hello() might be inlined properly
{
    b.hello();   // indirect, non-virtual call
}

void hello(const A& a)
{
    a.hello();   // Indirect virtual call, inlining is impossible in general
}

int main()
{
    B<None>  b;         // Ok, no vtable generated, empty base class optimization works, sizeof(b) == 1 usually
    B<None>* pb = &b;
    B<None>& rb = b;

    b.hello();          // direct call
    pb->hello();        // pb-relative non-virtual call (1 redirection)
    rb->hello();        // non-virtual call (1 redirection unless optimized out)
    t_hello(b);         // works as expected, one redirection
    // hello(b);        // compile-time error


    B<A>     ba;        // Ok, vtable generated, sizeof(b) >= sizeof(void*)
    B<None>* pba = &ba;
    B<None>& rba = ba;

    ba.hello();         // still can be a direct call, exact type of ba is deducible
    pba->hello();       // pba-relative virtual call (usually 3 redirections)
    rba->hello();       // rba-relative virtual call (usually 3 redirections unless optimized out to 2)
    //t_hello(b);       // compile-time error (unless you add support for const A& in t_hello as well)
    hello(ba);
}

The fun part of it is that you can now define interface and non-interface functions laterto defining classes. That is useful for interworking interfaces between libraries (don't rely on this as a standard design process of a singlelibrary). It costs you nothing to allow this for all of your classes - you might even typedefB to something if you'd like.

好玩的是,你现在可以定义接口和非接口功能,以定义类。这对于库之间的交互接口很有用(不要依赖它作为单个库的标准设计过程)。在所有课程中都允许这样做不会花费您任何费用 -typedef如果您愿意,您甚至可以B 到某事。

Note that, if you do this, you might want to declare copy / move constructors as templates, too: allowing to construct from different interfaces allows you to 'cast' between different B<>types.

请注意,如果您这样做,您可能还想将复制/移动构造函数声明为模板:允许从不同的接口构造允许您在不同B<>类型之间“转换” 。

It's questionable whether you should add support for const A&in t_hello(). The usual reason for this rewrite is to move away from inheritance-based specialization to template-based one, mostly for performance reasons. If you continue to support the old interface, you can hardly detect (or deter from) old usage.

您是否应该添加对const A&in 的支持是有问题的t_hello()。这种重写的通常原因是从基于继承的专业化转向基于模板的专业化,主要是出于性能原因。如果继续支持旧界面,则很难检测(或阻止)旧的使用。

回答by Galaxy

The virtualkeyword should be added to functions of a base class to make them overridable. In your example, struct Ais the base class. virtualmeans nothing for using those functions in a derived class. However, it you want your derived class to also be a base class itself, and you want that function to be overridable, then you would have to put the virtualthere.

virtual关键字应该被添加到一个基类的功能,使他们重写。在您的示例中,struct A是基类。virtual在派生类中使用这些函数没有任何意义。但是,如果您希望您的派生类本身也是一个基类,并且您希望该函数可以被覆盖,那么您就必须将它放在virtual那里。

struct B : public A {
    virtual void hello() { ... }
};

struct C : public B {
    void hello() { ... }
};

Here Cinherits from B, so Bis not the base class (it is also a derived class), and Cis the derived class. The inheritance diagram looks like this:

这里C继承自B,所以B不是基类(也是派生类),C是派生类。继承图如下所示:

A
^
|
B
^
|
C

So you should put the virtualin front of functions inside of potential base classes which may have children. virtualallows your children to override your functions. There is nothing wrong with putting the virtualin front of functions inside of the derived classes, but it is not required. It is recommended though, because if someone would want to inherit from your derived class, they would not be pleased that the method overriding doesn't work as expected.

因此,您应该将 放在virtual可能有子类的潜在基类中的函数前面。virtual允许您的孩子覆盖您的功能。将 放在virtual派生类中的函数前面没有错,但这不是必需的。不过还是推荐这样做,因为如果有人想从您的派生类继承,他们不会因为覆盖的方法没有按预期工作而感到高兴。

So put virtualin front of functions in all classes involved in inheritance, unless you know for sure that the class will not have any children who would need to override the functions of the base class. It is good practice.

因此,将virtual所有涉及继承的类中的函数放在函数前面,除非您确定该类不会有任何需要覆盖基类函数的子类。这是一个很好的做法。

回答by user2264698

I will certainly include the Virtual keyword for the child class, because

我肯定会为子类包含 Virtual 关键字,因为

  • i. Readability.
  • ii. This child class my be derived further down, you don't want the constructor of the further derived class to call this virtual function.
  • 一世。可读性。
  • ii. 这个子类可以进一步派生,你不希望进一步派生类的构造函数调用这个虚函数。