Overloading and Inheritance in C++, C#, and VB.NET

Let's consider these three pieces of code in C++, C# and VB.NET:

C++C#VB.NET
#include <stdio.h>

class Base
{
public:

  virtual void f(int x, int y) 
  { 
    printf("Base::f(%d,%d)\n", x, y); 
  }


  void f(int x) 
  { 
    f(x,x); 
  }
};

class Derived : public Base
{
public:

  virtual void f(int x, int y)
  { 
    printf("Derived::f(%d,%d)\n", x, y);
  }

};



int main()
{
  Derived D;
  D.f(3); // <-------- error!
  return 0;
}
using System;

class Base
{


  public virtual void f(int x, int y) 
  {
    Console.
      WriteLine("Base::f({0},{1})", x, y); 
  }

  public void f(int x) 
  { 
    f(x,x); 
  }
}

class Derived : Base
{


  public override void f(int x, int y)
  { 
    Console.
      WriteLine("Derived::f({0},{1})", x, y);
  }
}

class Program
{
  static void Main(string[] args)
  {
    Derived D = new Derived();
    D.f(3); // <-------- OK
  }
}
Imports System

Class Base



  Public Overridable _
  Sub f(ByVal x As Integer, ByVal y As Integer)
    Console. _
      WriteLine("Base::f({0},{1})", x, y)
  End Sub

  Public Sub f(ByVal x As Integer)
    f(x, x)
  End Sub

End Class

Class Derived
  Inherits Base


   Public Overrides _
   Sub f(ByVal x As Integer, ByVal y As Integer)
     Console. _
       WriteLine("Derived::f({0},{1})", x, y)
   End Sub
End Class

Class Program

  Shared Sub Main()
    Dim D As New Derived
    D.f(3) ' <-------- error!
  End Sub

End Class

They seem to be exactly equivalent, but are they? Only C# version works as expected and prints "Derived::f(3,3)". C++ and VB.NET versions give compilation error, saying that Derived::f does not take one argument. Why is that? Shouldn't the compiler automatically recognize that Base::f must be called in this case?

The short answer is no. Longer answer is "no, because of different name hiding rules in C++, C# and VB.NET

C++ : Hiding By Name

In C++ the situation is simple, albeit somewhat unexpected. Member of derived class hides all members of the base classes with the same name. Note, that signature (i.e. types of parameters), or accessibility of members (public, protected, or private) is not taken into account at all. Private member of derived class can hide public member of base class, as in the code below:

class Base
{
public:
    void f(int, int) {};
};

class Derived : public Base
{
private:
    void f() {};
};

void Test()
{
    Derived D;
    D.f(3,3); // <-------- error!
    return 0;
}

C# : Hiding By Signature

In C# name hiding is done by signature, i.e. Derived.f(int,int) may hide Base.f(int,int), but not Base.f(int). This is why our first example compiled and worked in C#.

There are two other differences between C# and C++ with respect to name hiding. Unlike C++, C# takes into account visibility of names. If a name is not accessible in particular scope, it will not hide a name, which is accessible. E.g., the following code will compile and will invoke Base.f():

using System;

class Base
{
    public void f() 
    {
        Console.WriteLine("Base.f()");
    }
}

class Derived : Base
{
    new private void f() 
    {
        Console.WriteLine("Derived.f()");
    }
}

class Program
{
    static void Main(string[] args)
    {
        Derived D = new Derived();
        D.f(); // <---------- OK
    }
}

The second difference between C# and C++ is that C# requires to use keyword new when name hiding occurs. If keyword new is omitted, C# compiler will issue a warning.

VB.NET : Hiding Of Your Choice

VB.NET is the most flexible among the three languages, and the most confusing. By default it follows the C++ model (hiding by name) with some relaxations. In particular, private member of derived class does not hide public member of base class:

Class Base
    Public Sub f(ByVal x As Integer)
        Console.WriteLine("Base.f({0})", x)
    End Sub
End Class

Class Derived
    Inherits Base

    Private Shadows Sub f()
        Console.WriteLine("Derived.F()")
    End Sub

End Class

Class Program
    Public Shared Sub Main()
        Dim D As New Derived
        D.f(3) ' <---------- OK
    End Sub
End Class

Note the keyword Shadows used to specify hiding by name. This is a default behavior, but without Shadows keyword VB.NET compiler will issue a warning.

VB.NET can be switched to hiding by signature by using Overloads keyword. For example, we can make our first example work, if we add Overloads to the definition of method Derived.f().

Imports System

Class Base

    Public Overridable Sub f(ByVal x As Integer, ByVal y As Integer)
        Console.WriteLine("Base::f({0},{1})", x, y)
    End Sub

    Public Sub f(ByVal x As Integer)
        f(x, x)
    End Sub

End Class

Class Derived
    Inherits Base

    Public Overloads Overrides Sub f(ByVal x As Integer, ByVal y As Integer)
        Console.WriteLine("Derived::f({0},{1})", x, y)
    End Sub

End Class

Class Program

    Shared Sub Main()
        Dim D As New Derived
        D.f(3) ' <-------- OK
    End Sub

End Class

Another interesting point about VB.NET is that Shadows and Overrides keywords cannot be used together (while Shadows and Overloads can). This creates an interesting situation. If we compile our original example from the top of this page, there will be a warning when defining Derived.f(int,int), because it shadows Base.f(int) (pardon me C#-like notation for signatures). Text of the warning recommends to add Shadows keyword to the definition, but if we do as told, we get error, saying Shadows and Overrides cannot be used together. This can be viewed as some lack of consistency in VB.NET language.