【问题标题】:When to mark a function in C++ as a virtual?何时将 C++ 中的函数标记为虚拟函数?
【发布时间】:2012-01-08 01:00:17
【问题描述】:

由于方法静态绑定的 C++ 特性,这会影响多态调用。

来自维基百科:

虽然这种调度机制涉及的开销很低,但它 对于该语言的某些应用领域可能仍然很重要 旨在针对。出于这个原因,Bjarne Stroustrup C++ 的设计者,选择使动态调度可选并且 非默认。只有使用 virtual 关键字声明的函数才会 基于对象的运行时类型调度;其他功能 将根据对象的静态类型进行分派。

所以代码:

Polygon* p = new Triangle;
p->area();

假设area() 是Parent 类中的non-virtual 函数,即Child 类中的overridden,则上面的代码将调用Parent's class method,这可能是开发人员所不期望的。 (感谢我介绍的静态绑定)

那么,如果我想编写一个供其他人使用的类(例如库),我是否应该将我的所有函数都设置为虚拟的,以便之前的代码能够按预期运行? p>

【问题讨论】:

  • p.area() 甚至无法编译。也许你的意思是p->area()?我想说,是时候拿起一本关于 C++ 的好书了。
  • 这取决于您所做的其他设计选择。作为开发人员,我不关心p->area() 是否调用Polygon::area()Triangle::area 或其他一些函数,只要它返回正确答案即可。这完全是图书馆的责任,对图书馆用户来说是不透明的。
  • 您可能会觉得this article 很有启发性。

标签: c++ polymorphism virtual dynamic-binding


【解决方案1】:

简单的答案是,如果您打算重写类的函数以实现运行时多态性,则应将它们标记为 virtual,如果您不打算这样做,则不要这样做。

不要仅仅因为你觉得它赋予了额外的灵活性就标记你的函数virtual,而是想想你的设计和暴露接口的目的。例如:如果您的类不是为继承而设计的,那么将您的成员函数设为虚拟会产生误导。一个很好的例子是标准库容器,它不应该被继承,因此它们没有虚拟析构函数。

没有理由不将所有成员函数标记为虚拟,引用一些性能损失,非 POD 类类型等等,但如果你真的打算有意 对于运行时重载,这就是它的目的以及所谓的缺陷。

【讨论】:

  • 我写了一个很大的答案,但我认为这总结了它。
  • 这意味着final 类只不应该包含虚方法?因为其他类可能会被其他人使用我的类继承
  • @Muhammad:我不确定你所说的final 类,C++ 中没有这样的概念,而且我不精通 Java。
  • Java 中的 final 类(C# 中的 steal)是不应被子类化的类(例如 1- 功能类或更好地表示为的类函数 - 好像 math.h 方法被放在某个类中,2- 出于安全原因不应子类化的类,例如 java.lang.String 类)
  • @Muhammad 与final 无关。这是一个班级设计的问题。除非您的代码被明确设计,以便该函数的实现可以变化(在指定的限制内)而不违反任何类不变量,否则该函数应该是虚拟的---在编写良好的 Java 中(非常罕见的东西),大多数函数应该声明为final。这是一个设计问题,在您解决之前,编写代码还为时过早。
【解决方案2】:

如果派生类应该能够覆盖该方法,则将其标记为虚拟。就这么简单。

【讨论】:

  • 有,也没有那么简单。
【解决方案3】:

作为一般规则,您应该只标记一个虚拟函数如果该类被明确设计为用作基类,并且该函数被设计为被覆盖。实际上,大多数虚函数在基类中都是纯虚函数。并且除了在调用反转的情况下,您明确不为覆盖函数提供合同,虚拟函数应该是私有的(或最多受保护的),并与执行合同的非虚拟函数一起包装。

【讨论】:

  • 保持公共函数非虚拟的另一个好处是它在调度之前提供了一个单一的断点位置。
【解决方案4】:

就内存性能而言,如果有任何东西是虚拟的,您将获得一个虚拟指针表,因此查看它的一种方式是“请一个,请所有”。否则,正如其他人所说,如果您希望它们可被覆盖,则将它们标记为虚拟,以便在基类上调用该方法意味着运行专用版本。

【讨论】:

    【解决方案5】:

    基本上就是这样;实际上,如果您使用的是父类,我认为您不需要重写每个方法,所以如果您认为您会以这种方式使用它,只需将它们设为 virtual

    【讨论】:

      猜你喜欢
      • 2012-04-18
      • 1970-01-01
      • 2013-05-20
      • 2018-02-01
      • 1970-01-01
      • 2021-01-08
      • 2013-01-11
      • 2017-06-10
      • 2011-08-12
      相关资源
      最近更新 更多