2

这个问题与覆盖虚函数和隐藏非虚函数有什么区别?,但我问的不是技术细节,而是非虚函数和虚函数的使用。

这里有一点背景。假设我有一个基类 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;
}

正如预期的那样,这将输出以下内容:

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

如果我在派生类中未实现虚函数 bar(),则派生类 B 和 C 中对 bar() 的任何调用都会解析为 A::bar()。xorp() 是一个非虚函数,也可以作为 b.xorp() 或 bA::xorp() 从派生类中调用。

例如,如果我要在 B 中实现 xorp(),它将有效地隐藏 A::xorp(),而对 b.xorp() 的调用实际上是对 bB::xorp() 的调用。

使用上面的示例,这让我想到了我的问题。假设我有一个派生类实现所需的辅助函数。

辅助函数是非虚拟成员函数(如 xorp())与辅助函数是派生类不覆盖的虚函数(bar()) 之间有区别吗?

通读关于类对象布局和 VTABLE 的演示文稿(https://www.cs.bgu.ac.il/~ashaf/SPL/Inheritance.pptx,幻灯片 28-35)我无法真正发现差异,因为两者都不是-virtual 和非覆盖的虚函数指向同一个地方(即基类中的函数)

谁能给我一个例子,这两种方法会产生不同的结果,或者如果有一个我没有发现的警告?

4

2 回答 2

3

您的示例中的缺陷是您没有使用多态性。您直接对所有对象进行操作。您不会注意到与覆盖相关的任何内容,因为不需要动态解析任何调用。如果调用不是动态解析的,那么虚函数和非虚函数绝对没有区别。要查看差异,请使用无辅助功能:

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

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

现在您应该能够看到对foo,bar和的调用如何baz解决的明显差异。尤其是...

例如,如果我要在 B 中实现 xorp(),它将有效地隐藏 A::xorp(),而对 b.xorp() 的调用实际上是对 bB::xorp() 的调用。

... 将不再是真实的里面baz

于 2018-11-11T12:36:38.243 回答
1

辅助函数是非虚拟成员函数(如 xorp())与辅助函数是派生类不覆盖的虚函数(bar())之间有区别吗?

如果您将方法标记为虚拟但从未覆盖它,则其行为将等同于您从未将其标记为虚拟。与它在对象中调用的其他方法的关系不受影响。

这并不是说仍然没有“差异”。

它肯定会向阅读代码的人传达意图上的差异。如果您的 xorp() 方法不是虚拟的——而是依赖虚拟方法来实现其行为——那么人们会将“xorpiness”理解为具有某些固定属性。他们可能会尝试避免在任何派生类中重新定义 xorp(),并且知道仅通过定义它所依赖的虚拟方法来间接影响 xorpiness。

另外,编译器并不总是知道您是否要使用虚拟覆盖。所以它不能优化虚拟调度的额外代码——即使你没有“利用”它。(有时它可以,就像如果你有一个你从不派生的类并且不导出它......虚拟可能会被丢弃。)

对于导出的类,您希望其他人使用:仅仅因为从不覆盖方法并不意味着其他人不会。除非你使用final并阻止你的对象的派生(这被认为不是非常友好,除非你有很好的理由)。因此,如果您将某些东西设为虚拟,则该功能就在那里,并且一旦其他人添加了覆盖,那么是的-届时将有所不同。

于 2018-11-11T12:58:32.310 回答