应该是一个新手问题...
我在现有的类A中有现有的代码,我想扩展它以覆盖现有的方法A :: f()。
所以现在我想创建类B来覆盖f(),因为我不想只改变A :: f(),因为其他代码依赖于它。
为此,我需要将A :: f()更改为虚拟方法,我相信。
我的问题是除了允许动态调用方法(使用B的实现而不是A)之外还有其他任何影响使方法成为虚拟的吗?我打破了某种优秀的编程习惯吗?这会影响试图使用A :: f()的其他代码吗?
请告诉我。
谢谢, JBU
编辑:我的问题更多的是让别人的方法虚拟有什么不对吗?即使你没有改变其他人的实现,你仍然需要进入某人的现有代码并对声明进行更改。
答案 0 :(得分:6)
如果你在基类中创建虚拟函数,那么从它派生的任何东西都会将它虚拟化。
虚拟化后,如果您创建A
的实例,则仍然会调用A::f
。
如果您创建B的实例并将其存储在A*
类型的指针中。然后你拨打A*::->f
,然后拨打B
的{{1}}。
至于副作用,除了轻微(不明显)的性能损失外,可能不会有任何副作用。
也有一个非常小的副作用,可能有一个C类也派生自A,它可能实现B::f
,并期望如果调用C::f
,那么它期望A*::->f
被召唤。但这并不常见。
但很可能,如果A::f
存在,那么它根本不会实现C
,在这种情况下,一切都很好。
但要小心,如果您使用的是已编译的库并且正在修改它的头文件,那么您期望工作的内容可能不会。您将需要重新编译标头和源文件。
您可以考虑采取以下措施以避免副作用:
C::f
的{{1}}类型,并将其设为A2
个虚拟
A
类型的指针代替f
A2
中导出A
。 根据您的需要,您也可以使用B
关系代替A2
。
答案 1 :(得分:4)
每次调用虚函数时,vtable查找都会有一个小的隐含性能损失。如果它不是虚拟的,则函数调用是直接的,因为代码位置在编译时是已知的。在运行时,必须从您正在调用的对象的vtable中引用虚函数地址。
答案 2 :(得分:4)
为此,我需要将A :: f()更改为 我相信是一种虚拟方法。
不,您不需要将其更改为虚拟方法以覆盖它。但是,如果您使用多态,则需要,即如果您有很多不同的类派生自A但存储为指向A的指针。
由于vtable(除了spoulson提到的内容),虚拟函数还有内存开销
答案 3 :(得分:2)
还有其他方法可以实现您的目标。 B
成为A
是否有意义?例如,让猫成为动物是有道理的,但猫不是狗。如果它们是相关的,也许A和B都应该来自基类。
是否只有通用功能可以分解?听起来像你永远不会以多态方式使用这些类,只是想要这个功能。我建议你把这个共同的功能拿出去,然后分成两个单独的类。
至于成本,如果您直接使用A ad B,编译将绕过任何虚拟调度,直接进入函数调用,就像它们从不虚拟一样。如果你将B
传递给期望'A1(作为参考或指针)的地方,那么就必须发送。
答案 4 :(得分:2)
在谈论虚拟方法时有2次性能点击。
答案 5 :(得分:2)
改变别人的代码是多么的犹豫,完全取决于当地的习俗和习俗。这不是我们可以为您解答的问题。
接下来的问题是这个类是否被设计为继承自。在许多情况下,类不是,并且将它们更改为有用的基类,而不改变其他方面,可能是棘手的。除了公共函数之外,非基类可能包含所有内容,因此如果您需要访问B中的更多内部,则必须对A进行更多修改。
如果您要使用B类而不是A类,那么您可以在不使其成为虚拟的情况下覆盖该功能。如果您要创建B类对象并将它们作为A的指针引用,那么您需要将f()设为虚拟。您还应该将析构函数设置为虚拟。
答案 6 :(得分:1)
在他们应得的虚拟方法中使用虚拟方法是一种很好的编程习惯。虚拟方法对于C ++类的合理性有很多含义。
如果没有虚函数,则无法在C ++中创建接口。接口是具有所有未定义虚函数的类。
然而,有时使用虚拟方法并不好。使用虚方法来改变对象的功能并不总是有意义的,因为它意味着子类。通常,您可以使用函数对象或函数指针来更改功能。
如上所述,虚函数创建一个表,运行程序将引用该表来检查要使用的函数。
C ++有很多难题,这就是为什么人们需要非常清楚他们想要做什么以及最好的做法是什么。与运行时动态OO编程语言(如Java或C#)相比,没有多少方法可以做到。有些方法要么是完全错误的,要么随着代码的发展最终导致未定义的行为。
因为你提出了一个非常好的问题:D,我建议你购买Scott Myer的书:Effective C ++,以及Bjarne Stroustrup的书:The C ++ Programming Language。这些将教会你C ++中OO的细微之处,特别是何时使用什么功能。
答案 7 :(得分:0)
如果那是该类将要拥有的第一个虚拟方法,那么你就不再是POD了。这可能会破坏事物,尽管这种可能性很小。