When you design public inheritance classes, you will mainly use three type of inheritance features.
- Pure virtual functions specify inheritance of interface only.
- Simple (impure) virtual functions specify inheritance of interface plus inheritance of a default implementation.
- Non-virtual functions specify inheritance of interface plus inheritance of a mandatory implementation.
Let me give an example to explain these in plain English
1 | class Shape { |
- Pure virtual function
draw
means bothRectangle
andEllipse
have to define their own implementation of draw. - Simple (impure) virtual function
error
means bothRectangle
andEllipse
have the option to defineerror
function. If they don’t, the default implementation in base classShape
will be used. - Non-virtual function
objectID
means bothRectangle
andEllipse
should not redefine this method and will use the implementation inShape
base class.
An interesting problem for simple virtual function is it specifies both interface and a optional default implementation. Sometimes such feature will cause problem because new derived class is added but forget to implement such function. Consider following class
1 | class Airplane { |
What if someone add ModelX
which derives from Airplane
, but ModelX
requires a special implementation of fly
in order for it to work correctly, but programmer forgets to implement such. Compiler will happily compile because the default implementation will be used. The poor programmer will only realize the mistake by running the program, hopefully not in production environment which may cost life in this case.
How to provide a default implementation as well as giving compilation error if someone forgets about it? There are two common approaches to this problem.
1 | class Airplane { |
By making fly
pure virtual, programmer will get error if they forget to implement such function in derived class. If they want to use the default behavior, they can simply call defaultFly
. By doing so, we separated the interface fly
and default implementation defaultFly
in base class.
Another solution is to implement fly
pure virtual function in base class.
1 | class Airplane { |
Don’t be surprised to see that you can actually provide an implementation for pure virtual class. This example is the exact reason compiler supports such feature. By doing so, we basically achieved making fly
mandatory and providing a default implementation.
The only slight difference between these two solutions is that defaultFly
can be protected method while implementation of pure fly
has to be public. In addition, if we provide default implementation for pure virtual function, people may not aware of such unless dig into cpp file, which they may assume to be empty for pure interface base class.