A derived class inherits its base class's members via class Derived : public Base. Crucially, a method only participates in runtime polymorphism (dynamic dispatch through a base pointer/reference) if it's marked virtual in the base class — without that keyword, C++ resolves the call at compile time based on the pointer's declared type, not the object's actual type, the opposite of Java's always-virtual default. The override keyword (C++11+) lets the compiler verify a method genuinely overrides a virtual base method, catching a signature mismatch that would otherwise silently create an unrelated new method instead.
Without virtual: polymorphism silently doesn't happen
Even though pet actually points to a Dog, calling makeSound() through the Animal-typed pointer calls Animal's version — the compiler resolved this at COMPILE time based on the pointer's declared type, since nothing here is marked virtual.
class Animal {
public:
void makeSound() { std::cout << "...\n"; } // NOT virtual
};
class Dog : public Animal {
public:
void makeSound() { std::cout << "Woof\n"; }
};
Animal* pet = new Dog();
pet->makeSound(); // prints "..." — the BASE version, NOT "Woof" — a real, common bugWith virtual: true runtime polymorphism
The single-word virtual change flips the behavior entirely — now the ACTUAL object's type decides which version runs, exactly like Java's default behavior.
class Animal {
public:
virtual void makeSound() { std::cout << "...\n"; } // virtual — enables polymorphism
virtual ~Animal() = default; // ALSO needs a virtual destructor — see below
};
class Dog : public Animal {
public:
void makeSound() override { // override: compiler VERIFIES this actually overrides
std::cout << "Woof\n";
}
};
Animal* pet = new Dog();
pet->makeSound(); // prints "Woof" — now genuinely polymorphic
delete pet;Virtual destructors: required, not optional, for polymorphic base classes
Without a virtual destructor, deleting a Dog through an Animal* only runs Animal's destructor, never Dog's — any resources Dog itself owns leak, and this is genuine undefined behavior, not just a style preference.
class Base {
public:
virtual ~Base() = default; // REQUIRED if any method is virtual and deletion happens via Base*
};
class Derived : public Base {
std::vector<int> data; // owns a resource
public:
~Derived() { /* cleans up data */ }
};
Base* obj = new Derived();
delete obj; // correctly calls Derived's destructor first, THEN Base's, because ~Base() is virtual