c++overridingvirtual-functionsname-hiding

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

#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(). xorp(), which is a non-virtual function, can also be called from the derived classes as either b.xorp() or b.A::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 b.B::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())?

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 (i.e the function in the base class)

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?


Solution

  • 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. 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 b.B::xorp().

    ... will no longer be true inside baz.