派生类中函数的 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
C++ "virtual" keyword for functions in derived classes. Is it necessary?
提问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 virtual
keyword 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 virtual
keyword 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 virtual
keyword 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
vf
is declared in a classBase
and in a classDerived
, derived directly or indirectly fromBase
, a member functionvf
with the same name, parameter-type-list (8.3.5), cv-qualification, and ref-qualifier (or absence of same) asBase::vf
is declared, thenDerived::vf
is also virtual (whether or not it is so declared) and it overridesBase::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 virtual
keyword 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 virtual
keyword 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 float
as 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 virtual
in 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 virtual
keyword 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 typedef
B 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 virtual
keyword should be added to functions of a base class to make them overridable. In your example, struct A
is the base class. virtual
means 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 virtual
there.
该virtual
关键字应该被添加到一个基类的功能,使他们重写。在您的示例中,struct A
是基类。virtual
在派生类中使用这些函数没有任何意义。但是,如果您希望您的派生类本身也是一个基类,并且您希望该函数可以被覆盖,那么您就必须将它放在virtual
那里。
struct B : public A {
virtual void hello() { ... }
};
struct C : public B {
void hello() { ... }
};
Here C
inherits from B
, so B
is not the base class (it is also a derived class), and C
is the derived class.
The inheritance diagram looks like this:
这里C
继承自B
,所以B
不是基类(也是派生类),C
是派生类。继承图如下所示:
A
^
|
B
^
|
C
So you should put the virtual
in front of functions inside of potential base classes which may have children. virtual
allows your children to override your functions. There is nothing wrong with putting the virtual
in 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 virtual
in 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. 这个子类可以进一步派生,你不希望进一步派生类的构造函数调用这个虚函数。