We have seen the importance of method overriding as one of the foundations of object-oriented programming, and Inheritance and Polymorphism.
Virtual methods are those methods defined in a base class that can be overridden by derived classes. It’s that simple.
By marking a method as virtual in the base class, it indicates that this method can be replaced by a different implementation in derived classes (providing their own implementation).
In contrast, we will call a method final one that cannot be overridden by derived classes (if we try to override them, the compiler or the IDE will flag an error).
On this particular point, different programming languages have different ways of applying these concepts. Not all languages follow the same criteria, although the basic concepts are the same.
In some languages, it is mandatory to explicitly mark methods that can be overridden as virtual (for example, by prefixing them with a keyword like virtual).
Other languages do not distinguish between virtual and non-virtual methods. In this way, all methods in these languages are implicitly virtual.
Finally, a few languages consider that by default methods are virtual, and those you don’t want to be overridden must be indicated with a reserved word like final or sealed.
Examples in different languages
Let’s see it with some real examples in different programming languages.
In C#, virtual methods are defined using the virtual keyword in the base class and are overridden in derived classes with the override keyword.
public class Animal
{
public virtual void HacerSonido()
{
Console.WriteLine("El animal hace un sonido");
}
}
public class Perro : Animal
{
public override void HacerSonido()
{
Console.WriteLine("Guau");
}
}
public class Gato : Animal
{
public override void HacerSonido()
{
Console.WriteLine("Miau");
}
}
// Usage
Animal dog = new Dog();
Animal cat = new Cat();
dog.MakeSound(); // Output: Woof
cat.MakeSound(); // Output: Meow
In C++, virtual methods are defined in the base class using the keyword virtual and overridden in the derived classes.
#include <iostream>
class Animal {
public:
virtual void MakeSound() const {
std::cout << "The animal makes a sound" << std::endl;
}
};
class Dog : public Animal {
public:
void MakeSound() const override {
std::cout << "Woof" << std::endl;
}
};
class Cat : public Animal {
public:
void MakeSound() const override {
std::cout << "Meow" << std::endl;
}
};
// Usage
int main() {
Animal* dog = new Dog();
Animal* cat = new Cat();
dog->MakeSound(); // Output: Woof
cat->MakeSound(); // Output: Meow
delete dog;
delete cat;
return 0;
}
In JavaScript, there is no specific syntax for virtual methods like in other languages, all methods are virtual.
class Animal {
makeSound() {
console.log("The animal makes a sound");
}
}
class Dog extends Animal {
makeSound() {
console.log("Woof");
}
}
class Cat extends Animal {
makeSound() {
console.log("Meow");
}
}
// Usage
let animal1 = new Dog();
let animal2 = new Cat();
animal1.makeSound(); // Output: Woof
animal2.makeSound(); // Output: Meow
In TypeScript, like JavaScript, all methods are virtual.
class Animal {
makeSound(): void {
console.log("The animal makes a sound");
}
}
class Dog extends Animal {
makeSound(): void {
console.log("Woof");
}
}
class Cat extends Animal {
makeSound(): void {
console.log("Meow");
}
}
// Usage
const dog: Animal = new Dog();
const cat: Animal = new Cat();
dog.makeSound(); // Output: Woof
cat.makeSound(); // Output: Meow
In Python, again all methods are virtual by default.
class Animal:
def make_sound(self):
print("The animal makes a sound")
class Dog(Animal):
def make_sound(self):
print("Woof")
class Cat(Animal):
def make_sound(self):
print("Meow")
# Usage
dog = Dog()
cat = Cat()
perro.HacerSonido(); // Salida: Guau
gato.HacerSonido(); // Salida: Miau
