Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / Java

Overriding Virtual Functions, a Comparison Between C#, C++ and Java

4.89/5 (10 votes)
20 Jan 2017CPOL5 min read 28.9K  
This article compares the OO features of inheritance and function overriding in C++, C# and Java.

Introduction

I use three different OO languages all the time, and all of them obviously support inheritance and late binding or virtual functions. I thought a nice article might also be a good reminder to myself when I switch between them about how Java, C# and C++ differ in terms of their syntax and default behaviour.

Background

I am constantly swapping between C++ for the Frostbite video games engine I work on and C# for the tools and web services. I also use Java for my Android home projects (the last one was an ant nest simulator for Swarm Ibtelligence - coming soon ?)

So I thought I would note to myself, the main differences between using the three languages in terms of inheritance and function over-riding (or virtual functions).

Using the Code

Accessibility

C++ supports public, protected and private inheritance. But whatever way it's done, don't forget that anything private is always private and does not become accessible to any inheriting or deriving class.

Java simplified this and only allows public inheritance.

C# copied Java in this respect.

It is generally agreed that you can achieve protected and private inheritance by using encapsulation and other existing features in OO languages, without the need for these other complex permutations.

Multiple Inheritance

Now a little bit about multiple inheritance. When building class hierarchies, C++ uses just a colon for inheritance, and following that can be a comma separated list of other base classes. C++ is the only language that supports multiple inheritance, meaning things like the deadly diamond of death can occur if you are not careful. Basically, the DDD is a design where more than one instance of the same class type is part of the object. Imagine class Dog derives from classes Animal and Pet, and both of these derive from a class called Printable, the instance of Dog now has two instances of the base class Printable. So what happens when you change the attributes of this base class type in the dog class ? Does it change instance 1 or instance 2's attributes etc.To avoid this type of complexity in hierarchies (and we normally favor composition over inheritance anyway - combinatorial explosion being one reason), C# and Java both chose not to support MI.

Interfaces

Years ago in the land where C++ was king, you could use an abstract base class (a class that cannot be instantiated, as it contains at least one pure virtual function) and put all your pure virtual functions in there to define a contract. This meant you could force a class deriving from your class to implement these functions. This was the birth of the interface. In C# and Java you can implement these as interfaces, thus giving you a kind of multiple inheritance which is basically a contract only  (or function signatures that you must implement).

C# supports both interfaces (which are similar to abstract base classes in C++, class containing all pure virtual functions), and abstract classes which can have one or more pure virtual functions, and also some functions with actual implementation. Remember all functions inside interface and abstract classes must be public, this is so they can be accessed in calling classes, there would not be much point in having a private function on an interface, who could call it ? There is no implementation in the class so it could not call it's own function ! So, C# covers all thee abstract, interface and concrete classes.

Let's take a look at some code that declares classes in all three languages with inheritance and interfaces:

C++

C++
class derived : public base, base2 //C++ class definition with 2 public inherited classes

Java

Java
class Derived extends Base implements printable // new derived class that derives from base, and implements the printable contract

C#

C#
public interface printable //A contract interface
public abstract class baser //A class with one or more abstact functions
public class derived : baser, printable //A derived class from baser, it implements the printable interface

Virtual Functions

Another apsect of object orientated code is polymorphism. The idea is a type can be constructed at run time, at compile type we only know the hierarchy it will be part of, ie we have a base type pointer, the ninstantiate a derived class in the hierarchy. Using this late-binding enables us to call fucntions that are decided at run-time not compile time. All three languages support late-binding and therefore virtual functions. Let's talk about each one.

C++

The old wise one by default has its functions set to non virtual. That is, if you have a base and a derived class, both of which have a function called print(), then it doesn't matter what type of instance you have constructed, you will call the function on the class depending on what type your reference or pointer is, not what you are pointing at. So to use virtual functions, you must declare a function virtual (anywhere in the hierarchy is fine), this tells the compiler to create a virtual function table, and to look up the address of the function in this table depending on what type you are when running. Once the function is marked virtual, the instance type will be the deciding factor as to which version of the funciton you call, not the type of the reference or pointer.

Java

Now Java went super simple, everything is virtual, no need for any statements to tell the run time engine to make something virtual, if a derived class and base class both declare a function called Print, with the same signature, then the instance type will decide which one the JRE uses. Super simple! That is of course unless you don't want this feature (maybe security is an issue ie you dont want the void AddMoneyToBankAccount() function to be overridden), in which case you mark the function final, and nothing can override it then, damn !

C#

So lastly C#, all functions in this saloon here are non-virtual, and not only that, if you want to override a function, it must be marked as virtual in the base, and the overriding function must be marked as override. You can however, have two functions with the same signature, in a related hierarchy, and not mark them with override, you just mark the derived one with new. You are then explicitly telling the compiler that this is intentional, you want to hide the base functions implementation, and it is not a virtual function.  so if you call the function with a Base pointer, it will always call the base pointer class types function, even if you point it at an instance of derived, look at it like the function is statically linked by the compiler, not dynamically by the run time (even though its C# so there is no compiler per se).

And finally some code to demonstrate all the virtual and overriding behaviour!!

C++ Code

C++
#include "stdafx.h"
#include <iostream>

using namespace std;

class base
{

public:
    void hello()
    {
        cout << "Base func non-virtual" << endl;
    }

    virtual void hello2()
    {
        cout <<"Base func virtual"<<endl;
    }
};

class derived : public base //C++ class definition with 2 inherited classes
{
public:
    void hello()
    {
        cout << "Derived func non-virtual" << endl;
    }

    virtual void hello2()
    {
        cout <<"Derived func virtual"<<endl;
    }

};

int main()
{
    cout << "calling function defined in both base and der that is NOT virtual, 
             then calling a function that is virtual" << endl;
    {
        cout << "Instance is Derived - Reference is Derived" << endl;
        derived d;
        d.hello();
        d.hello2();
    }
    {
        cout << "Instance is Derived - Reference is Base" << endl;
        base *f = new derived ;
        f->hello();
        f->hello2();
    }
    {
        cout << "Instance is Base - Reference is Base" << endl;
        base t;
        t.hello();
        t.hello2();
    }
    {
        cout << "Instance is Derived - reinterpret cast to base - Reference is Derived" << endl;
        derived *g = new derived ;
        base *j = reinterpret_cast<base*>(g);
        j->hello();
        j->hello2();
    }
    
    getchar();
    return 0;
}

And the output:

calling function defined in both base and der that is NOT virtual, 
then calling a function that is virtual
Instance is Derived - Reference is Derived
Derived func non-virtual
Derived func virtual
Instance is Derived - Reference is Base
Base func non-virtual
Derived func virtual
Instance is Base - Reference is Base
Base func non-virtual
Base func virtual
Instance is Derived - reinterpret cast to base - Reference is Derived
Base func non-virtual
Derived func virtual

And C#

C#
using System;

namespace ConsoleApplication2
{
    public class based
    {
        public void hello()
        {
            Console.WriteLine("hello on base instance - non virtual on base");
            Console.WriteLine("func is - non virtual on deriv");
        }

        public virtual void hello2()
        {
            Console.WriteLine("hello2 on base instance - virtual on base");
            Console.WriteLine("func is  - virtual on deriv");
        }

        public void hello3()
        {
            Console.WriteLine("hello3 on base instance - non virtual on base");
            Console.WriteLine("func is new and virtual on deriv");
        }

        public virtual void hello4()
        {
            Console.WriteLine("hello4 on base instance - virtual on base");
            Console.WriteLine("func is new and virtual on deriv");
        }

        public virtual void hello5()
        {
            Console.WriteLine("hello5 on base instance - virtual on base");
            Console.WriteLine("func is override on deriv");
        }

        public void hello6()
        {
            Console.WriteLine("hello6 on base instance - NOT virtual on base");
            Console.WriteLine("func is override on deriv");
        }
    }

    public class derived : based
{
        public void hello()
        {
            Console.WriteLine("hello on deriv - non virtual on deriv");
            Console.WriteLine("func is non virtual on base");
        }

        public virtual void hello2()
        {
            Console.WriteLine("hello2 on deriv - virtual on deriv");
            Console.WriteLine("func is virtual on base");
        }

        public new virtual void hello3()
        {
            Console.WriteLine("hello3 on deriv - new and virtual on deriv");
            Console.WriteLine("func is non virtual on base");
        }

        public new virtual void hello4()
        {
            Console.WriteLine("hello4 on deriv - new and virtual on deriv");
            Console.WriteLine("func is virtual on base");
        }

        public override void hello5()
        {
            Console.WriteLine("hello5 on deriv - override on deriv");
            Console.WriteLine("func is virtual on base");
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("base ref with base inst");
            based b = new based();
            b.hello();
            b.hello2();
            b.hello3();
            b.hello4();
            b.hello5();
            Console.WriteLine();

            Console.WriteLine("deriv ref with deriv inst");
            derived d = new derived();
            d.hello();
            d.hello2();
            d.hello3();
            d.hello4();
            d.hello5();
            Console.WriteLine();

            Console.WriteLine("base ref with deriv inst");
            based o = new derived();
            o.hello();
            o.hello2();
            o.hello3();
            o.hello4();
            o.hello5();
            Console.WriteLine();

            Console.WriteLine("deriv ref with deriv inst cast back to base ref");
            derived s = new derived();
            based ds = s;
            ds.hello();
            ds.hello2();
            ds.hello3();
            ds.hello4();
            ds.hello5();

            Console.WriteLine("The end--");

            Console.ReadLine();
        }
    }
}

And the output:

base ref with base inst
hello on base instance - non virtual on base
func is - non virtual on deriv
hello2 on base instance - virtual on base
func is  - virtual on deriv
hello3 on base instance - non virtual on base
func is new and virtual on deriv
hello4 on base instance - virtual on base
func is new and virtual on deriv
hello5 on base instance - virtual on base
func is override on deriv

deriv ref with deriv inst
hello on deriv - non virtual on deriv
func is non virtual on base
hello2 on deriv - virtual on deriv
func is virtual on base
hello3 on deriv - new and virtual on deriv
func is non virtual on base
hello4 on deriv - new and virtual on deriv
func is virtual on base
hello5 on deriv - override on deriv
func is virtual on base

base ref with deriv inst
hello on base instance - non virtual on base
func is - non virtual on deriv
hello2 on base instance - virtual on base
func is  - virtual on deriv
hello3 on base instance - non virtual on base
func is new and virtual on deriv
hello4 on base instance - virtual on base
func is new and virtual on deriv
hello5 on deriv - override on deriv
func is virtual on base

deriv ref with deriv inst cast back to base ref
hello on base instance - non virtual on base
func is - non virtual on deriv
hello2 on base instance - virtual on base
func is  - virtual on deriv
hello3 on base instance - non virtual on base
func is new and virtual on deriv
hello4 on base instance - virtual on base
func is new and virtual on deriv
hello5 on deriv - override on deriv
func is virtual on base
The end--

And lastly Java

Java
package com.roboticsfordreamers;

class Base implements
{
    public void Hello()
    {
        System.out.println("Hello this the base");
    }
}

class Derived extends Base
{
    public void Hello()
    {
        System.out.println("Hello this the derived");
    }
}

public class Main {

    public static void main(String[] args) {
        Base b = new Base();
        Derived d = new Derived();
        Base b2 = new Derived();

        b.Hello();
        d.Hello();
        b2.Hello();
    }
}

And the output :

Hello this the base
Hello this the derived
Hello this the derived

Process finished with exit code 0

I hope you find this a useful comparison and reference.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)