从Java的角度来看,我惊讶地发现,您只能重写具有虚拟关键字的基本方法。在爪哇中,使用最后一个关键字声明无法重写的方法。
我脑子里有个想法,就是你很少想禁止凌驾于世,这样别人就可以根据自己的情况来扩展你的课程。
所以在C++中,如果你觉得某人可能想在某个阶段继承你的班级(也许几年后有人认为这是一个很酷的想法),你会让你的方法都是虚拟的吗?
或者是有一些重要的原因,想要禁止在C++中,我没有意识到这一点?
作为参考,这是我对每种语言所做的实验:
爪哇
public class Base {
void doSomething(){
System.out.println("Doing the base thing");
}
}
public class Derived extends Base {
void doSomething(){
System.out.println("Doing the derived thing");
}
public static void main(String... argv){
Base object = new Derived();
object.doSomething();
}
}
C++
class Base
{
public:
Base(void);
~Base(void);
virtual void doSomething();
};
void Base::doSomething(){
std::cout <
" << std::endl;
}
class Derived :
public Base
{
public:
Derived(void);
~Derived(void);
void doSomething();
};
void Derived::doSomething(){
std::cout <
}
int main(void){
Base * object = new Derived;
object->doSomething();
return 0;
}
何时将C++中的函数标记为虚拟的可能副本?
达菲莫和艾尔斯正把你带向正确的方向。我只是想对你说的一件事发表评论:
So in C++ if you feel that someone might want to at some stage inherit
from your class (maybe years later someone thinks its a cool idea) do
you make all your methods virtual?
从软件工程的角度来看:如果您没有立即使用继承,也没有使用接口,那么我不建议将方法声明为虚拟的。
虚拟方法带来了非常轻微的性能下降。对于非关键代码路径,性能影响可能可以忽略不计。但是对于经常被调用的类方法,它可以加起来。编译器不能做太多的内联和直接链接。相反,必须在运行时在v-table数组中查找要调用的虚拟方法。
当我的编码团队中有人开始与"稍后可能会有人想要…"进行设计对话时,我的"未来校对"反模式警报就会响起。设计可扩展性是一回事,但"面向未来的特性"应该推迟到那时。
此外,多年后那个认为这是一个很酷的想法的人,让他自己把类方法转换成虚拟的。不管怎么说,到那时你将进入更大的项目。:)
请注意,虚方法的性能命中仅适用于C++,或者如果实际上有不同的重写方法(甚至可以在优化中内联,等等)。
是的,你必须使你所有的方法都是虚拟的。
Java认为默认情况下一切都是公平的游戏,而禁止它需要采取行动。C++和C语言的观点截然相反。
是的,在C++中,一个类方法只能是EDOCX1×0,如果它在基类中被标记为EDCOX1×1。
如果您的类是为继承而创建的,并且您的类方法打算为基和派生提供不同的行为,那么将该方法标记为virtual。
良好阅读:
何时将C++中的函数标记为虚拟?
来自于虚函数缺省的语言的程序员往往会惊讶于C++具有逆向选择的默认值,即非虚拟是默认值。但是,请注意,[具体]虚拟函数的契约很难记录和测试,因为实际上涉及两个不同的契约:
所有覆盖函数的契约,即函数在概念上的作用。
具体功能发生作用的契约
只记录其中的一个并不能真正减少它,因为另一半根本不清楚。这也意味着您不能从具体的实现中瞥一眼实际的合同是什么(假设您处于一种不那么不典型的情况下,文档是由源提供的)。
举个例子,为什么所有的东西都是虚拟的或继承的可能不是一个很好的想法,实际上,经常会看到那些从EDCOX1 8继承的人,他们在爪哇并没有像他们想象的那样。
即使没有virtual也可以重写基类中的方法。
以这个小程序为例:
#include
struct A
{
void m()
{ std::cout <
"; }
virtual void n()
{ std::cout <
"; }
};
struct B : public A
{
void m()
{ std::cout <
"; }
virtual void n()
{ std::cout <
"; }
};
int main()
{
A a;
a.m();
a.n();
B b;
b.m();
b.n();
A &c = b;
c.m();
c.n();
}
程序输出为:
A
A
B
B
A
B
如您所见,方法B::m重写了A中的相同方法。但这只在使用B的精确实例时才有效。在第三种情况下,当使用对基类的引用时,需要virtual使其工作。
virtual关键字是overidding所必需的,或者你得到的是Function hiding关键字。派生结构B中的方法m()隐藏了基础结构A方法,而B中的方法n()覆盖了基础结构方法。
在B中对m()所做的操作称为重载(这也会导致基类版本被隐藏),而不是重写。在C++中,函数必须显式声明为虚拟函数,使其可重写。