In C++, the virtual keyword, when applied to class methods, aids in polymorphism. If a method is declared to be virtual, the most derived version of the method is executed when a call is made. If a method is non-virtual, the specific version that is called depends on how the method is called. If the method is called via a pointer to the base class, the base class method is called, if by the derived class, then the derived method is called. This definition is weak; an example is better, as usual:
#include <iostream>
#include <string>
struct Base {
// Non virtual method
int getInt() { return 1; }
// Virtual Method
virtual std::string getString() { return "Base"; }
};
struct Derived : Base {
int getInt() { return 2; }
virtual std::string getString() { return "Derived"; }
};
int main() {
Derived d;
Base &baseref(d);
Base *baseptr(&d);
Derived &derivedref(d);
Derived *derivedptr(&d);
// Calling by the object itself, the expected version is called
std::cout << d.getInt() << std::endl; // 2
std::cout << d.getString() << std::endl; // "Derived"
// Calling by a reference or pointer to the base class,
// non-virtual methods call the base class version
// virtual methods call the derived version
std::cout << baseref.getInt() << std::endl; // 1
std::cout << baseref.getString() << std::endl; // "Derived"
std::cout << baseptr->getInt() << std::endl; // 1
std::cout << baseptr->getString() << std::endl; // "Derived"
// Calling by a reference or pointer to the derived class
// The derived versions are always called
std::cout << derivedref.getInt() << std::endl; // 2
std::cout << derivedref.getString() << std::endl; // "Derived"
std::cout << derivedptr->getInt() << std::endl; // 2
std::cout << derivedptr->getString() << std::endl; // "Derived"
}
A Note About Virtual Destructors
Taking the above classes:
Base *b = new Derived();
delete b; // Warning, non-virtual destructor
If we were to delete a Derived by a pointer to Base, only the Base destructor is called! This means the Derived destructor is not called and the object is not properly freed. Most modern compilers will warn if you create this situation. The fix to it is simple. You most simply provide virtual destructors:
struct Base
{
// We have virtual methods, so we should provide a virtual destructor
virtual ~Base()
{
}
// Non virtual method
int getInt()
{
return 1;
}
// Virtual Method
virtual std::string getString()
{
return "Base";
}
};
struct Derived : Base
{
virtual ~Derived()
{
}
int getInt()
{
return 2;
}
virtual std::string getString()
{
return "Derived";
}
};