Prev | Index | Next |
Inheritance and subtyping
C* makes a distinction between subtyping and inheritance. It's possible to extend a class A with a class B, for ease of implementation, but to have no subtyping relation defined - so that the typechecker won't allow you to assign a B to a variable of type A. This feature was introduced because of the fact that conceptually, not all derived classes introduce "is-a" relations, even if mechanically it looks like the derived class is an instance of the superclass. So it's up to the programmer to specify whether an "is-a" relationship exists.
To inherit from a class without introducing a subtype, use the "extends" keyword:
class ColourPoint extends Point { ... };
To specify that ColourPoint is both a subclass and a subtype, use the subtype operator:
class ColourPoint <: Point { ... };
Unlike C++, it is not necessary to write the "public" keyword to specify public inheritance; inheritance is public by default.
Overriding
To override a function in C*, you must declare it a function case:
class Point { function draw(); }; class ColourPoint { function case draw(); };
This restriction prevents accidental overriding, as well as misspelling the function name when overriding it and inadvertently introducing a new function.
Functions are "virtual" by default - that is, any plain member function can be overridden - whereas C++ required functions to be labelled "virtual" to enable overriding. To prevent a function from being overridden, label the function "final". (C* has both final and const keywords. The former means "can't be extended", while the latter means "can't be modified".)
The keyword "virtual" is now used for pure virtual functions (abstract classes) exclusively. To declare an abstract class, label the functions that need to be implemented "virtual":
class Shape { virtual function draw(); //the "= 0" syntax from C++ is no longer used };
It is an error to try to give an implementation to a function declared "virtual".
Multiple inheritance
Multiple inheritance is present in C*, but is slightly more restrictive than in C++, because it disallows ambiguities resulting from diamond inheritance. That is, if a class inherits from multiple superclasses that each define a particular function, it's an error. The error can be resolved by simply overriding the ambiguous member.
For example:
class A { function f() => int { return 0; } }; class B <: A { function case f() => int { return 1; } }; class C <: A { function case f() => int { return 2; } }; class D <: B, C {}; //Error - which f does D inherit?
If the programmer decides that D's f should behave like A's, they could resolve the ambiguity by overriding it to do so:
class D <: B, C { function case f() => int { return A.f(); } };
Prev | Index | Next |