什么时候适合使用虚方法?
我了解虚拟方法允许派生类覆盖从基类继承的方法。什么时候适合/不适合使用虚方法?并不总是知道一个类是否会被子类化。难道一切都应该虚拟化,只是“以防万一”吗?或者这会导致巨大的开销吗?
I understand that virtual methods allow a derived class to override methods inherited from a base class. When is it appropriate/inappropriate to use virtual methods? It's not always known whether or not a class will be sub classed. Should everything be made virtual, just "in case?" Or will that cause significant overhead?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(10)
首先,稍微迂腐一点——在 C++ 标准中,我们称它们为成员函数,而不是方法,尽管这两个术语是等效的。
我看到不将成员函数设为虚拟的两个原因。
final
关键字] 更好)。这也与意图有关。如果您无意以多态方式使用该类,则不要将任何内容设为虚拟。如果您任意将成员设为虚拟,您就会滥用里氏替换原则并且这些类别的错误很难追踪和解决。不要通过预先将成员函数设为虚拟来过早地悲观你的类。
First a slightly pedantic remark - in C++ standardese we call them member functions, not methods, though the two terms are equivalent.
I see two reasons NOT to make a member function virtual.
final
keyword] which is even better). It's also about intent. If it's not your intent to use the class polymorphically, don't make anything virtual. If you arbitrarily make members virtual you are inviting abuses of the Liskov Substitution Principle and those classes of bugs are painful to track down and solve.Don't prematurely pessimize your class by pre-emptively making member functions virtual.
当您设计一个类时,您应该非常清楚它是否代表一个接口(在这种情况下,您将适当的可重写方法和析构函数标记为虚拟),或者它打算按原样使用,可能与其他对象组合或组合。
换句话说,您对课程的意图应该是您的指南。将所有内容都虚拟化通常是矫枉过正,有时甚至会误导哪些方法旨在支持运行时多态性。
When you design a class you should have a pretty good idea as to whether it represents an interface (in which case you mark the appropriate overrideable methods and destructor virtual) OR it's intended to be used as-is, possibly composing or composed with other objects.
In other words your intent for the class should be your guide. Making everything virtual is often overkill and sometimes misleading regarding which methods are intended to support runtime polymorphism.
这是一个棘手的问题。但有一些指导方针/经验法则需要遵循。
virtual
方法,一旦需要派生,只将需要的那些方法设为virtual
在子类中自定义。virtual
方法,那么析构函数应该是virtual
(讨论结束)。我认为这些很简单。我绝对让 ABI 部分的反射消失,它仅在交付 DLL 时有用。
It's a tricky question. But there are some guidelines / rule of thumbs to follow.
virtual
method, once you need to derive, only makevirtual
those methods you need to customize in the child class.virtual
method, then the destructor shall bevirtual
(end of discussion).virtual
method non-public and provide public wrappers in charge of assessing pre and post conditions, so that derived classes cannot accidentally break them.I think those are simple enough. I definitely let the ABI part of the reflexion away, it's only useful when delivering DLLs.
如果您的代码遵循特定的设计模式,那么您的选择应该反映 DP 自己的原则。例如,如果您正在编写装饰器模式,则应该是虚拟的函数是属于Component接口。
否则,我想遵循一种渐进的方法,IOW,在我看到层次结构试图从您的代码中出现之前,我没有虚拟方法。
If your code is following a particular design pattern, then your choice should reflect the DP's own principles. For example, if you are coding a Decorator pattern, the function that should be virtual are the ones that belong to the Component interface.
Otherwise, I'd like to follow an evolutional approach, IOW I don't have virtual methods until I see that a hierarchy is trying to emerge from your code.
例如,Java 中的成员函数是 100% 虚拟的。在 C++ 中,它被视为代码大小/函数调用时间的损失。此外,非虚函数保证函数实现始终相同(使用基类对象/引用)。 Scott Meyers 在《Effective C++》中对此进行了更详细的讨论。
For instance member functions in Java are 100% virtual. In C++ it is considered as a code size/function call time penalty. Additionally a non virtual function guarantees that the function implementation will always be the same (using the base class object/reference). Scott Meyers in "Effective C++" discusses it in more details.
我主要使用的健全性测试是 - 如果我定义的类是将来派生的,行为(功能)是否保持不变或者是否需要重新定义。如果是,那么该函数是虚拟的有力竞争者,如果不是,则不是,如果我不知道 - 我可能需要研究问题域以更好地理解我计划实现的行为。大多数问题域都会给我答案 - 如果没有,则行为通常是不重要的。
A sanity test I mostly use is - In case a class I'm defining is derived from in future, would the behavior (function) remain same or would it need to be redefined. If it would be, the function is a strong contender for being virtual, if not then no, if I do not know - I probably need to look into the problem domain for better understanding of behavior I'm planning to implement. Mostly problem domain gives me the answer - in cases where it does not the behavior is generally non critical.
我想快速确定的一种可能方法是考虑您是否要处理一堆类似的类,您将使用它们来执行相同的任务,而更改是您的方式去做那些任务。
一个简单的例子是计算各种几何图形的面积问题。您需要正方形、圆形、矩形、三角形等的面积,这里唯一改变的是用于计算面积的数学公式(方式)。因此,让这些形状中的每一个都从公共基类继承,并在基类中添加一个返回面积的虚拟方法(然后您可以使用各自的数学公式在每个子项中实现),这将是一个不错的决定。
“以防万一”将所有内容都虚拟化将使您的对象占用更多内存。此外,调用虚函数时会有少量(但非零)开销。因此,恕我直言,当性能/内存限制很重要时(这基本上意味着您编写的每个实际程序),将所有内容都虚拟化“以防万一”将是一个坏主意。
然而,根据需求的阐明程度以及预期代码更改的频率,这又是有争议的。例如,在快速而肮脏的工具或初始原型中,一些额外的内存字节和几毫秒的时间损失并没有多大意义,因此拥有一堆(不必要的)虚拟函数是可以的的灵活性。
I guess one possible way to determine quickly would be to consider if you are going to deal with a bunch of similar classes that you are going to use to perform the same tasks, with the change being the way you go about doing those tasks.
One trivial example would be the problem of computing areas for various geometric figures. You need the areas of squares, circles, rectangles, triangles etc. and the only thing that changes here is the math formulae (the way) you use to compute the area. Therefore it would be a good decision to have each of these shapes inherit from a common base class and to add a virtual method in the base class that returns the area (which you can then implement in each of the child with the respective math formula).
Making everything virtual "just in case" will make your objects take up more memory. Additionally, there is a small (but non-zero) overhead when calling virtual functions. So, IMHO, making everything virtual "just in case" would be bad idea when performance/memory constraints are important (which basically means in always every real-world program that you write).
However, this again is debatable based on how clearly the requirements are spelled out and how often code-changes expected. For example, in a quick-and-dirty tool or an initial prototype where a few extra bytes of memory and a few milliseconds of lost time do not mean much, it would be OK to have a bunch (unnecessarily) virtual functions for the sake of flexibility.
我的观点是,如果你想使用父类指针指向子类实例并使用它们的方法,那么你应该使用虚方法。
My point is if you want to use the parent class pointer to point at child class instance and use their methods, then you should use virtual methods.
虚方法是实现多态性的一种方法。当您想要在更抽象的级别定义某些操作,以致由于它太笼统而无法实际实现时,可以使用它们。只有在派生类中,您才能知道如何执行该操作。但通过虚拟方法的定义,您创建了一个要求,这增加了类层次结构的刚性。这可能是明智的,也可能不是,这取决于您想要获得什么以及您自己的品味。
Virtual methods are a way to achieve polymorphism. They are used when you want to define some action at a more abstract level such that it is impossible to actually implement because it is too general. Only in derived classes you can tell how to perform that action. But with the definition of a virtual method you create a requirement, that adds rigidity to the hierarchy of classes. This can advisable or not, it depends on what you are trying to obtain, and on your own taste.
查看设计模式。如果您的代码/设计是其中之一或类似的,请使用虚函数。否则,请尝试这个
Have a look at Design Patterns. If your code/design is one of these or similar go use virtual function. Otherwise, try this