Don’t forget that virtual

I am not really a C++ programmer. I usually code in C, and I think all C++ I’ve ever written involved a couple of vectors and maybe one or two classes. So what I’m writing here is certainly old news for C++ programmers.

The other day, while I was reading this essay about the Liskov Substitution Principle, I was intrigued by a situation presented in page 4, which deals with class inheritance and method overriding.

Methods that can be overriden must be declared virtual in C++. I really didn’t know about that. I am used to Java’s behaviour, which I illustrate below:

/*
 * A.java
 */
 
public class A {
 
	public void bar() {
		System.out.println("bar");
	}
}
 
/*
 * B.java
 */
 
public class B extends A {
 
	public void bar() {
		System.out.println("Bar!");
	}
}
 
/*
 * C.java
 */
 
public class C {
 
	public void call(A o) {
		o.bar();
	}
}
 
/*
 * Main.java
 */
 
public class Main {
 
	public static void main(String [] args) {
		A a = new A();
		B b = new B();
		C c = new C();
 
		c.call(a);
		c.call(b);
	}
}

The code above will produce the following output:

bar
Bar!

If I wanted to do the same thing in C++, I would have to write it this way:

#include <iostream>
 
class A {
public:
	A() {}
	virtual ~A() {}
	virtual void bar() { std::cout << "bar" << std::endl; }
};
 
class B : public A {
public:
	B() {}
	virtual ~B() {}
	void bar() { std::cout << "Bar!" << std::endl; }
};
 
class C {
public:
	C() {};
	virtual ~C() {};
	void call(A &o) { o.bar(); }
};
 
int main(int argc, char *argv[]) {
	A a;
	B b;
	C c;
 
	c.call(a);
	c.call(b);
 
	return 0;
}

The output will be the same as the Java program, but pay attention to the virtual modifier placed before the declaration of the bar method in A. If I remove it, the output will be:

bar
bar

Even though B declares a method called bar, it is completely shadowed by the implementation inherited from A. In my opinion, Java’s behaviour is a lot more intuitive. I wonder how many people might have spend hours, maybe days, looking for a bug in a C++ program when the problem was that a method was not declared virtual and method calls weren’t occuring as expected.

I talked about this with Otávio, and he found out C# has a similar characteristic. In C#, a method must be declared as virtual for its subclasses to override it AND the subclasses must use override when overriding the method. Here’s the code he provided me:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
 
namespace ConsoleApplication1
{
   class Alice
   {
       public virtual void sayHello()
       {
           Console.WriteLine("Hello, world");
       }
   }
 
   class Bob : Alice
   {
       public override void sayHello()
       {
           Console.WriteLine("Hello, world!");
       }
   }
 
   class Echo
   {
       public void say(Alice toto)
       {
           toto.sayHello();
       }
   }
 
   class Program
   {
       static void Main(string[] args)
       {
           Alice a = new Alice();
           Bob b = new Bob();
           Echo e = new Echo();
 
           e.say(a);
           e.say(b);
       }
   }
}

Some people argue that this makes code clearer, but I’m not convinced. Why not make overriding implicit?

One Comment

  1. Iuri Fiedoruk says:

    I do agree that virtual should not be necessary, and most modern languages such as PHP does not require it for override.

Leave a Reply