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:
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:
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?