可能重复:
Overriding vs Virtual
在C ++中,无论您是否选择使用虚拟,您仍然可以覆盖基类功能。以下编译就好了......
class Enemy
{
public:
void SelectAnimation();
void RunAI();
void Interact()
{
cout<<"Hi I am a regular Enemy";
}
private:
int m_iHitPoints;
};
class Boss : public Enemy
{
public:
void Interact()
{
cout<<"Hi I am a evil Boss";
}
};
所以我的问题是使用或不使用虚函数有什么区别。而且缺点是什么。
答案 0 :(得分:18)
如果你有代码:
Enemy * p = new Boss;
p->Interact();
并且Interact不是虚拟的,你将得到Enemy的Interact。换句话说,将根据明显而不是正在调用的事物的实际类型来选择函数。这几乎不是您想要的,所以如果您打算通过基指针调用方法(例如,如果您在向量或列表中有一组基本指针),那么该函数应该在基类中变为虚拟。您还需要将此类基类的析构函数设置为虚拟,以便通过基本指针删除实例时的行为是明确定义的。
答案 1 :(得分:6)
如果Enemy::Interact()
未声明virtual
,则从基类中的成员函数调用Enemy::Interact()
或通过指向基类的指针或引用将不会调用派生类{ {1}}功能。
例如:
Interact()
如果您将Boss boss;
Enemy* bossEnemy = &boss;
boss.Interact(); // calls Boss::Interact()
bossEnemy->Interact(); // calls Enemy::Interact()
声明为Enemy::Interact()
,那么virtual
将按照您的预期进行调用。
使用虚拟功能的缺点是,与非虚拟功能相比,它们可能更昂贵。不使用虚函数的缺点是您可能无法获得所需的结果。
答案 2 :(得分:3)
您使用游戏开发标记了问题,在这种情况下,忽略虚拟呼叫的额外呼叫开销可能是仓促的:Elan Ruskin measured 50% increase在呼叫开销中。同一个人(和许多其他游戏开发者)consider it a good practice只有在你有一个具体的理由时才能使用虚拟功能的附加灵活性,而不只是为了它的乐趣。
以下是technical writeup额外费用的原因,以及对纯虚拟功能的额外费用的一些考虑。
答案 3 :(得分:0)
virtual
功能使C ++面向对象。这是您首先使用C ++的主要原因之一。 如果您的设计需要,请不要再考虑使用virtual
。不要仅仅为避免虚拟而重新设计模型。
即使增加了从结构基础跳转到内存偏移量的成本,您是否会考虑访问结构字段?不,如果设计需要它,你当然不会。您是否会考虑传递回调,事件监听器,仿函数或任何其他需要跳转到达实际数据的“逻辑”地址?如果设计需要它,你当然不会。
另一方面,如果设计没有调用它,那么将类成员设置为虚拟是没有意义的,就像设计不需要它时不需要传递仿函数或不必要地创建结构一样。 决定是否使用virtual
是优秀OO设计和实施的一部分。
<强>性能强>
关于所谓的绩效成本:首先,这是一个非常古老的问题。虚拟调用的早期C ++实现的性能实际上可以在没有令人难以置信的代码的情况下进行测量。正如其他人所提到的,今天的技术在很大程度上已经废除了这场辩论。
其次,矢量乘法和类似设计的例子具有误导性。他们出现来衡量虚拟呼叫和非虚拟呼叫之间的区别。但他们不是。他们正在衡量数十亿虚拟呼叫与数十亿非虚拟呼叫之间的差异,这些呼叫几乎不做任何事情。是否存在可能容易受此问题影响的真实代码?这当然是可能的。当你找到它时,解决方案是否会成为一般使用虚拟替罪羊的替罪羊?显然不是。解决方案是优化对性能特别敏感的代码。作为这个假设优化的一部分,虚拟的删除是明智的,但不会给你太多。如果您的代码 对性能敏感,那么您需要优化的不仅仅是丢弃虚拟内容。
第三,它很容易衡量,这很好,因为你不需要接受我们的话。您可以轻松地在目标架构上对编译器的差异进行基准测试,以确保确实没有性能差异。