在C++中,有什么真正的理由不将成员函数设置为虚函数吗?当然,在大多数情况下,虚函数的开销相对较低,因此性能问题似乎并不会粘着在程序上。
另一方面,我曾经遇到过几次忘记将本应该是虚函数的函数设置为虚函数的情况。这似乎比性能问题更为重要。所以,默认情况下将成员函数设置为虚函数是否存在任何理由不这样做呢?
在C++中,有什么真正的理由不将成员函数设置为虚函数吗?当然,在大多数情况下,虚函数的开销相对较低,因此性能问题似乎并不会粘着在程序上。
另一方面,我曾经遇到过几次忘记将本应该是虚函数的函数设置为虚函数的情况。这似乎比性能问题更为重要。所以,默认情况下将成员函数设置为虚函数是否存在任何理由不这样做呢?
语言设计者 Stroustrup 说:
因为许多类并不是设计成基类来使用的。例如,参见class complex。
此外,具有虚函数的类的对象需要虚函数调用机制所需的空间 - 通常每个对象需要一个字。这种开销可能很大,并且可能妨碍与其他语言(如 C 和 Fortran)的数据布局兼容性。
有关更多设计理念,请参阅《C++的设计与演化》。
有几个原因。
首先是性能:虚函数的开销相对较低,但它也阻止了编译器进行内联,这是C++中一个巨大的优化源。C++标准库之所以表现出色,是因为它可以内联其中包含的许多小型一行函数。此外,具有虚方法的类不是POD数据类型,因此会受到许多限制。它不能仅通过memcpy复制,构造它更加昂贵,占用更多空间。涉及非POD类型时,很多事情突然变得非法或不那么高效。
其次,符合良好的面向对象编程实践。类的重点在于它进行某种抽象、隐藏其内部细节,并提供保证:“这个类将如此如此地行事,并始终保持这些不变量。它永远不会处于无效状态。”如果允许其他人覆盖任何成员函数,这就非常难以做到。在类中定义的成员函数存在的目的是确保不变量的维护。如果我们不关心这一点,那就可以将内部数据成员设为public,并让人们任意操纵它们。但我们希望我们的类是一致的。这意味着我们必须指定其公共接口的行为。这可能涉及特定的自定义点,例如使单个函数虚拟化,但它几乎总是需要使大多数方法非虚拟的,以便它们可以完成确保不变量的工作。非虚拟接口惯用语就是一个很好的例子: http://www.gotw.ca/publications/mill18.htm
第三,继承通常不是必需的,特别是在C++中。模板和泛型编程(静态多态性)在许多情况下比继承(运行时多态性)更好。是的,有时仍然需要虚方法和继承,但这肯定不是默认选项。如果是这样,你就做错了。与其试图假装它是其他语言,不如与语言一起工作。C++不是Java,与Java不同,在C++中,继承是例外而不是规则。
http://www.gotw.ca/publications/mill18.htm
关于NVI惯用语的评论:
http://www.parashift.com/c++-faq-lite/strange-inheritance.html#faq-23.3 http://accu.org/index.php/journals/269 [见子部分]