为什么我要用C ++标记所有虚拟方法?有没有权衡?

时间:2014-08-31 17:13:49

标签: c++ virtual-method

我知道虚拟方法为何以及如何工作,并且大多数时候人们告诉我应该始终将方法标记为虚拟,但是,我不明白为什么我不打算覆盖它。而且我也知道存在微小的内存问题。

请解释一下为什么我应该将所有方法标记为虚拟和什么是权衡。

代码示例:

class Point
{
   int x, y;
   public:
      virtual void setX(int i);
      virtual void setY(int i);
};

(这个问题不等于Should I mark all methods virtual?,因为我想知道权衡,因为案例中的编程语言是C ++,而不是C#)

OBS:如果有任何语法错误,我很抱歉,英语不是我的母语。

7 个答案:

答案 0 :(得分:5)

不,你不应该把所有方法标记为虚拟"。

如果您希望该方法是虚拟的,请将其标记为。如果没有,请将关键字保留。

与常规方法相比,虚拟方法存在开销。如果您想了解更多相关信息,请查看维基百科方面的VTables

答案 1 :(得分:4)

使成员函数非虚拟化的真正原因是强制执行类不变量。

使所有成员函数虚拟化的建议通常意味着:

  1. 提供建议的人不理解课程,或
  2. 提供建议的人不了解OO设计。
  3. 是的,有一些情况(例如,一些抽象基类,其中唯一的类不变量是特定函数的存在),其中所有函数都应该是虚拟的。这些都是例外。在大多数类中,虚函数应限于那些您真正打算允许派生类提供新/不同行为的函数。

    关于像vtables这样的事情和虚拟函数调用的开销的讨论,我说他们已经纠正了,他们错过了真正的大点。特定功能是应该还是不应该是虚拟的主要是类设计的问题,其次只是函数调用开销的问题。两个人不做同样的事情,所以试图比较开销很少有意义。

答案 2 :(得分:2)

情况并非如此,即如果您不需要虚拟功能,则不要使用它。同样根据 Bjarne Stroustrup Pay per use

  

在C ++中: -

     
      
  1. 虚拟功能会有轻微的性能损失。通常它太小而不能产生任何差别,但可能是紧密的循环   显著。

  2.   
  3. 虚函数通过一个指针增加每个对象的大小。这通常是微不足道的,但如果你创造   数以百万计的小物件可能是一个因素。

  4.   
  5. 具有虚函数的类通常意味着继承自。派生类可以替换部分,全部或不替换   虚函数。这可能会产生额外的复杂性   复杂性是程序员的致命敌人。例如,派生的   class可能很难实现虚函数。这可能会破坏一部分   依赖于虚函数的基类。

  6.   

答案 3 :(得分:1)

使虚拟方法具有轻微的成本(更多的代码,更多的复杂性,更大的二进制文件,更慢的方法调用),并且如果该类不是从它继承而没有带来任何好处。类需要设计用于继承,否则从它们继承只是乞求射击自己的脚。所以不,你不应该总是让每个方法都是虚拟的。那些告诉你这个的人可能只是过于继承了。

答案 4 :(得分:1)

C ++的基本原则之一就是你不会为你不需要的东西买单。 virtual函数花费比时间和空间中的普通成员函数更多。因此,无论你是否真的需要它们,你都不应该总是使用它们。

答案 5 :(得分:0)

并非所有功能都应标记为虚拟。

实际上,有一种强制执行前/后条件的模式,明确要求公共成员虚拟。它的工作原理如下:

class Whatever
{
public:
  int frobnicate(int);
protected:
  virtual int do_frobnicate(int);
};

int Whatever::frobnicate(int x)
{
  check_preconditions(x);
  int result = do_frobnicate(x);
  check_postconditions(x, result);
  return result;
}

由于派生类不能覆盖公共函数,因此无法删除前/后条件检查。但是,他们可以覆盖执行实际工作的受保护do_frobnicate

答案 6 :(得分:-1)

(编辑-我是通过重复的C#问题来解决这个问题的,但是我认为答案还是有用的!编辑来反映这一点:)

实际上,C#具有很好的“正式”理由:https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/versioning-with-the-override-and-new-keywords

第一句话是:

  

设计C#语言是为了使不同库中的基类和派生类之间的版本控制能够发展并保持向后兼容性。

因此,如果您正在编写一个类,则最终用户可能会生成派生类,并且不同的版本可能会失去同步...突然之间,它变得很重要。如果您已经仔细保护了代码的核心方面,那么如果您进行更新,人们仍然可以(希望)使用旧的派生类。

另一方面,如果您还好,在他们的作者更新到最新版本之前,没有人能够使用派生类,则所​​有内容都可以是虚拟的。我已经在几种允许修改的“抢先体验”游戏中看到了这种情况-当游戏版本增加时,突然许多MOD都被破坏了,因为它们依赖于以一种方式起作用的事物……并且它们发生了变化。 (好吧,并非所有更改都与此相关,但有些更改与此相关。)

这实际上取决于您的使用情况。如果人们可以继续使用您的旧版本,也许他们不在乎您是否已对其进行了更新,并乐于将其与派生类一起使用。在大型企业中,将所有内容虚拟化很可能是某人更新某些内容时一次破坏很多内容的秘诀。

这是否也适用于C ++?我不明白为什么不这样做-C ++也用于大型项目,并且还会面临多个同时版本的危险。