What is a "virtual" declaration?

jimLee:
@gfvalvo I.. I think your totally missing what I was saying.

I understand now. Your 'rect' is what would be known as a "bounding box" in computer graphics. It's a way to simplify hit detection. With any shape, the points outside the bounding box are easy to calculate and eliminate as a hit. For some non-rectangular shapes, the bounding box is often close enough. For example on a rounded-rectangle button, it's not that important if the corner bits outside the curves are counted as 'in'.

@johnwasser : Exactly! In fact, I use this example because I've been dealing with this for quite awhile. LC_GUIbase.

-jim lee

aarg:
...and does not, and can not have, any implementation in the base class... IIRC

Not so. It can be implemented in the superclass and have its implementation overridden in a subclass.

aarg:
IDRC :frowning: ... but can't you override a method in a base class, without 'virtual'?

Yes, but you get different results when you use a base class pointer to access an object of the derived class. With a virtual function, you get the derived class version when you use a base class pointer. Without the 'virtual' keyword, you get the base class version (Example below).

Imagine in my example above of Drawable->Rectngle->Square that you have a Picture class that contains a list of Drawable pointers. Those pointers can point to rectangles, squares, circles, octagons... whatever drawable objects you want to define. The Picture class just has to tell each Drawable to "draw()" and, because draw() is virtual, the derived objects will do the drawing.

class Base
{
  public:
    virtual void MyClass()
    // void MyClass()
    {
      Serial.println("My class is Base.");
    }
};

class Derived : public Base
{
  public:
    void MyClass()
    {
      Serial.println("My class is Derived.");
    }
};


Base BaseObject;
Derived DerivedObject;

// A base class pointer can point to any derived class.
Base *pBase = &DerivedObject;


void setup()
{
  Serial.begin(115200);
  BaseObject.MyClass();
  DerivedObject.MyClass();
  pBase->MyClass();
}
void loop() {}

Output when Base::MyClass() is 'virtual':

My class is Base.
My class is Derived.
My class is Derived.

Output when Base::MyClass() is NOT 'virtual':

My class is Base.
My class is Derived.
My class is Base.

aarg:
IDRC :frowning: ... 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

bperrybap:
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.

Yes, but using virtual requires V-table creation which increases a RAM usage and slightly slower execution speed. Since microcontrollers like atmega 328 have a very limited RAM size it's better to not use a virtual if you don't need a polimorphism. Most arduino libraries users do not use base class pointer and pointers at all.

Yes, but using virtual requires V-table creation which increases a RAM usage and slightly slower execution speed. Since microcontrollers like atmega 328 have a very limited RAM size it's better to not use a virtual if you don't need a polimorphism. Most arduino libraries users do not use base class pointer and pointers at all.

Yeah, well.. Some of us do.

And you know. That's just throwing ghosts. You can still get a LOT of great utility out of dynamic ram usage, pointers, virtual methods etc. on a small platform like an Arduino. Just keep track of what you're doing. It not like your coding with strychnine.

-jim lee

alesam:
Yes, but using virtual requires V-table creation which increases a RAM usage and slightly slower execution speed. Since microcontrollers like atmega 328 have a very limited RAM size it's better to not use a virtual if you don't need a polimorphism. Most arduino libraries users do not use base class pointer and pointers at all.

For the most part I agree.
While Arduino uses C++ most code doesn't take advantage of many of the C++ capabilities.

RAM does increase. It also increases code size a bit - potentially a lot depending on if the virtual functions are used.
The big hurt with virtual functions is that, a few years ago, gcc removed a "hack" that could be used to allow the linker to remove unused virtual functions.
As of now, ALL virtual functions (base class default and derived override version) will be linked in to the final image regardless of whether they are ever called or not.
So there is a cost associated with virtual functions and in some cases it can be quite high.

IMO, gcc should seriously consider adding a capability of removing unused/un-called virtual functions, particularly the pure virtual ones. It should work similar to the way uncalled normal functions can be removed.
This is an issue not just for small code/ram environments.

But even on micro controllers, it sometimes still makes sense to use virtual functions and some of the more advanced C++ stuff.

Also, these days, the AVR is quite long in the tooth.
It is very resource constrained vs some of the other alternatives at similar or even lower price points that are 32 bit, much faster, have megabytes of flash, and LOTS more RAM than the AVRs.

--- bill

aarg:
IDRC :frowning: ... but can't you override a method in a base class, without 'virtual'?

You can, but it won't be polymorphic. The method you get will depend on the static compile-time type of the variable, not the runtime type. This matters when you are using references and pointers.

class Vehicle {
public:
  void go() { Serial.println("Vehicle, go!); }
  virtual void goV() { Serial.println("Vehicle, go virtual!); }
}

class Car: public Vehicle {
public:
  void go() { Serial.println("Car, go!); }
  virtual void goV() { Serial.println("Car, go virtual!); }
}

Vehicle myVehicle;
Car myCar;

void setup() {
  Serial.begion(9600);
  while(!Serial);

  Serial.println("a reference to a plain vehicle");
  Vehicle& v = myVehicle;
  v.go();
  v.goV();

  Serial.println("a reference to a plain car");
  Car& c = myCar;
  c.go();
  c.goV();

  Serial.println("a reference to a vehicle that is actually a car");
  v = myCar;
  v.go();
  v.goV();
}

void loop(){}