On Wednesday, 14 April 2021 at 13:43:20 UTC, Berni44 wrote:
> I'm trying to understand, what virtual functions are. I found the specs, but I can't make head or tail of it.
- What is a
vtbl[]
? Obviously a function pointer table. But where to find this? The examples don't use it. Maybe something inside of the compiler?
- Which of the eight functions in the example are virtual and and which not? OK B.abc is said to be virtual, as the comment states. But it seems never to be used. And why is it considered to be virtual?
- There is also the term "covariant function", which is not explained. What is this?
Recommended reading: https://en.wikipedia.org/wiki/Liskov_substitution_principle
This is all related to object-oriented programming and class inheritance. Because we can put a subclass object into a superclass variable (class Child : Parent { }; Parent parent = new Child;
), we cannot look at the type of an object variable to decide which methods to call, because the object itself may be of a subtype. As such, when we call a method foo
on Parent
, the compiler looks up the class info in a pointer in the first 8 bytes of the object, finds the method pointer for foo
, and calls it with the object as a hidden parameter. (This is the this
field.)
So a virtual method is a method that is called "virtually", as compared to directly by name, by turning the method name into a function pointer call via the classinfo.
The list of function pointers for methods in the class info is called the virtual method table, or vtable.
Covariance is related to the Liskov principle, and just means that because Child
can be treated as a Parent
, a method that returns Parent
in the superclass can be overridden (its vtable pointer replaced with a new one) by one that returns a Child
in the subclass. In other words, as "Child class replaces Parent class", the "return type Child
" can replace the "return type Parent
"; ie. in the child class you can use a child class of the return type, ie. they "vary together" - covariance.
The opposite (contravariance) happens for parameters: if a superclass method takes Child
, the subclass can take Parent
instead - again, because Child
can turn into Parent
per Liskov.
A different way to think about this is that method parameter and return types form a contract that is defined by the superclass and fulfilled by the subclass, and the subclass can relax the call contract ("I demand from my caller") and restrict the return contract ("I promise my caller"). Since the Child
, by Liskov, can do everything the Parent
can do, demanding less - ie. a Parent
instead of a Child
- keeps the superclass's call contract valid, and promising more - ie. returning a Child
instead of a Parent
, which may have additional capabilities - keeps the superclass's return contract valid.