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
drawmeans bothRectangleandEllipsehave to define their own implementation of draw. - Simple (impure) virtual function
errormeans bothRectangleandEllipsehave the option to defineerrorfunction. If they don’t, the default implementation in base classShapewill be used. - Non-virtual function
objectIDmeans bothRectangleandEllipseshould not redefine this method and will use the implementation inShapebase 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.