简体   繁体   English

使用非虚拟基类函数与派生类未实现虚拟函数之间的区别

[英]Difference between using non-virtual base class functions versus derived class non-implemented virtual functions

This question is slightly related to What are the differences between overriding virtual functions and hiding non-virtual functions? 这个问题与覆盖虚拟功能和隐藏非虚拟功能有什么区别? , but I'm not asking about the technical details, rather about the usage of non-virtual and virtual functions. ,但我不是在询问技术细节,而是在询问非虚函数和虚函数的用法。

Here's a little background. 这是一些背景。 Let's say I have a base class A and two derived classes B and C 假设我有一个基类A和两个派生类B和C

#include <iostream>

class A {
public:
  A() {};
  virtual void foo() { std::cout << "foo() called in A\n"; };
  virtual void bar() { std::cout << "bar() called from A\n"; };
  void xorp() { std::cout << "xorp() called from A\n"; };
  virtual ~A() {};
};

class B : public A {
public:
  B() {};
  virtual ~B() {};
  virtual void foo() override { std::cout << "foo() called in B\n"; };
  //virtual void bar() override not implemented in B, using A::bar();
};

class C : public A {
public:
  C() {};
  virtual ~C() {};
  virtual void foo() override { std::cout << "foo() called in C\n"; };
  //virtual bar() override not implemented in C, using A::bar();
};

int main() {
  A a{};
  B b{};
  C c{};

  a.foo(); //calls A::foo()
  a.bar(); //calls A::bar()
  a.xorp(); //calls non-virtual A::xorp()

  b.foo(); //calls virtual overridden function B::foo()
  b.bar(); //calls virtual non-overridden function A::bar()
  b.xorp(); //calls non-virtual A::xorp()

  c.foo(); //calls virtual overridden function C::foo()
  c.bar(); //calls virtual non-overridden function A::bar()
  c.xorp(); //calls non-virtual A::xorp()

  return 0;
}

This outputs, as expected, the following: 如预期的那样,将输出以下内容:

foo() called in A
bar() called from A
xorp() called from A
foo() called in B
bar() called from A
xorp() called from A
foo() called in C
bar() called from A
xorp() called from A

If I leave the virtual function bar() unimplemented in the derived classes, any call to bar() in the derived classes B and C gets resolved to A::bar(). 如果我在派生类中未实现虚拟函数bar(),则派生类B和C中对bar()的任何调用都会解析为A :: bar()。 xorp(), which is a non-virtual function, can also be called from the derived classes as either b.xorp() or bA::xorp(). xorp()是非虚拟函数,也可以从派生类中作为b.xorp()或bA :: xorp()进行调用。

If I were to implement a xorp() in B, for example, it would effectively hide the A::xorp() and a call to b.xorp() would actually be a call to bB::xorp(). 例如,如果我要在B中实现xorp(),它将有效地隐藏A :: xorp(),而对b.xorp()的调用实际上就是对bB :: xorp()的调用。

Which brings me to my question, using the example above. 使用上面的示例,这使我想到了我的问题。 Let's say I have a helper function that the derived classes need for their implementation. 假设我有一个辅助函数,派生类需要实现该函数。

Is there a difference between having the helper function be a non-virtual member function (like xorp()), versus the helper function being a virtual function that the derived classes do not override (bar()) ? 将helper函数作为非虚拟成员函数(例如xorp())与将helper函数作为派生类不重写的虚拟函数(bar())之间有区别吗?

Reading through this presentation on class object layout and VTABLEs ( https://www.cs.bgu.ac.il/~asharf/SPL/Inheritance.pptx , slides 28-35) I could not really spot a difference, since both non-virtual and non-overridden virtual functions point to the same place (ie the function in the base class) 阅读有关类对象布局和VTABLE的演示文稿( https://www.cs.bgu.ac.il/~asharf/SPL/Inheritance.pptx ,幻灯片28-35),我无法真正发现差异,因为两者都不-virtual和不可覆盖的虚函数指向同一位置(即基类中的函数)

Could anyone give me an example where these two approaches would produce different results, or if there is a caveat that I have not spotted? 谁能给我一个例子,说明这两种方法会产生不同的结果,或者是否有我未发现的警告?

The flaw in your example is that you aren't using polymorphism. 您的示例中的缺陷在于您没有使用多态。 You operate on all the objects directly. 您可以直接对所有对象进行操作。 You won't notice anything related to overriding, because none of the calls need to be resolved dynamically. 您不会注意到与覆盖相关的任何内容,因为不需要动态解决所有调用。 And if the call is not resolved dynamically, there is absolutely no difference between virtual functions and non-virtual ones. 如果调用不是动态解决的,则虚拟函数和非虚拟函数之间绝对没有区别。 To see the difference, use a helper free function: 若要查看区别,请使用无辅助函数:

void baz(A& a) {
  a.foo();
  a.bar();
  a.xorp();
}

int main() {
  // As before
  baz(a);
  baz(b);
  baz(c);
}

Now you should be able to see a noticeable difference in how the calls to foo , bar and baz are resolved. 现在您应该能够看到在解决foobarbaz调用方面的显着差异。 In particular... 尤其是...

If I were to implement a xorp() in B, for example, it would effectively hide the A::xorp() and a call to b.xorp() would actually be a call to bB::xorp(). 例如,如果我要在B中实现xorp(),它将有效地隐藏A :: xorp(),而对b.xorp()的调用实际上就是对bB :: xorp()的调用。

... will no longer be true inside baz . ...在baz内部将不再是真实的。

Is there a difference between having the helper function be a non-virtual member function (like xorp()), versus the helper function being a virtual function that the derived classes do not override (bar())? 将帮助程序函数作为非虚拟成员函数(例如xorp())与将帮助程序函数作为派生类不重写的虚拟函数(bar())之间有区别吗?

If you mark a method virtual but you never override it, it's behavior will be equivalent to if you'd never marked it virtual. 如果将方法标记为虚拟但从未覆盖它,则其行为等同于从未将其标记为虚拟的方法。 The relationship to other methods it calls in the object is not affected. 与它在对象中调用的其他方法的关系不受影响。

That isn't to say there aren't still "differences". 这并不是说仍然没有“差异”。

It certainly conveys a difference in intent to those reading the code. 对于阅读代码的人来说,它的意图肯定有所不同。 If your xorp() method is not virtual--but relies on virtual methods to implement its behavior--then people will understand "xorpiness" as having certain fixed properties. 如果您的xorp()方法不是虚拟的,而是依靠虚拟方法来实现其行为,那么人们将把“ xorpiness”理解为具有某些固定属性。 They would likely try to avoid a redefinition of xorp() in any derived classes, and know to only affect the xorpiness indirectly through defining the virtual methods it depends on. 他们可能会尝试避免在任何派生类中重新定义xorp(),并且知道仅通过定义它所依赖的虚拟方法来间接影响xorpiness。

Plus, the compiler can't always know if you're going to use a virtual override or not. 另外,编译器并不总是知道您是否要使用虚拟替代。 So it can't optimize out the extra code for virtual dispatch--even if you aren't "taking advantage" of it. 因此,即使您没有“利用”它,也无法优化用于虚拟调度的额外代码。 (Occasionally it can, like if you have a class you never derive from and don't export it...the virtual may just get dropped.) (有时候,它可以,例如,如果您有一个从不派生且不导出的类,则可能会删除该虚拟类。)

And with exported classes you expect other people to use: just because you never overrode a method doesn't mean someone else won't. 对于导出的类,您希望其他人使用:仅仅因为从未覆盖过某个方法并不意味着其他人不会使用。 Unless you use final and prevent derivation of your objects (which isn't considered terribly friendly to do, unless you've got really good reasons) . 除非您使用final并防止对象派生(除非确实有充分的理由,否则这样做并不十分友好) So if you make something virtual, the ability is there, and once someone else adds an override then yes--there will be a difference at that point. 因此,如果您将某项虚拟化,那么功能就存在了,一旦其他人添加了替代项,那么就可以了–那会有所不同。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM