aarg:
IDRC
... but can't you override a method in a base class, without 'virtual'?
Yes but as John mentioned if you don't use virtual then funny and undesired things can happen when you use pointers or references of the base class.
For this reason, I would say you typically don't want to override a base class function that isn't virtual.
virtual ensures that a derived class can override a function in a base class and ensures it will be called when called from both the base class and the derived class(es).
If you don't use virtual then you end up with different functions being called depending on how you call it.
Here is an example of how virtual is necessary.
Suppose you want to create a function that can do printf() style formatting on any device that uses the Print class for its library object.
Here is an example sketch that does this using a function called Pprintf() that works like fprintf() but instead of FILE pointer it takes a pointer/reference to a device object.
void setup(void)
{
Serial.begin(115200);
Pprintf(Serial, "Pprintf...\n");
}
void loop(void)
{
Pprintf(Serial, "Seconds up: %04ld\n", millis()/1000);
delay(1000);
}
#define PPRINTF_BUFSIZE 64
size_t Pprintf(Print &outdev, const char *format, ...)
{
char buf[PPRINTF_BUFSIZE];
va_list ap;
va_start(ap, format);
vsnprintf(buf, sizeof(buf), format, ap);
va_end(ap);
return(outdev.write(buf));
}
The Pprintf() function can work with any object that inherits the Print class.
I.e. LCD devices, serial ports, network devices, etc...
Because the write() function in the Print class is virtual, the Pprintf() code can reference the write() function from the base class of the object without having to know the actual type of the object being used.
i.e. it calls the function in the base class which in turn calls the function in the derived class.
Had the Print class not used virtual, calls to write() using the actual object would work but calls using the Print base class would not.
i.e. Serial.print() would call the write() function in HardwareSerial, but outdev.write() in Pprintf() would call the write() function inside the Print class which would be some default function and would not call the HardwareSerial write() function so it would not output any characters on the serial port.
The magic that makes all this work, is that C++ allows you to pass a pointer to an object the same as if it were pointer to an inherited base class.
For example, even though you may call a function using a pointer/reference to a HardwareSerial class object (like Serial), a function that expects a Print class pointer/reference can receive it and use the Print class functions without knowing anything about HarwareSerial or the Serial object since HardwareSerial inherits the Print class.
This is a feature of C++
--- bill