Multiple inheritance refers to the ability of a class to have more
than a single superclass. Several languages provide multiple
inheritance; equally many languages provide only single
inheritance, possibly with `interface inheritance' constructs like
Objective-C's protocols and Java's interfaces. C++, in all its
baroqueness, provides both multiple inheritance, plus inheritance
of interface through signatures.
Semantics
Suppose the class D has both B and
C as a superclass. What effect does this have?
- Any state defined for instances of
B or C is
also present in instances of D . There is no sharing
of slots based on the name of instance variables as in CLOS.
Thus, every instance variable i consuming space in an
instance of B or C , also consumes space
in an instance of D .
- Every method defined for
B is also defined for
D . Obviously, every method defined for
C is also defined for D .
- If both
B and C define the same method
foo , a method clash is said to have occured, and
D should provide its own implementation of that
method. This is not mandatory; it is not checked by the compiler;
it is optionally checked by the resolver. If a method clash is
not resolved, a program-condition is raised when the
method is invoked at run time.
A class with instances that carry state (i.e., instance variables)
must be a subclass of the State class. If both
B and C maintain state, they must both
inherit from State , which brings up the issues
involving repeated inheritance. Actually, these issues are mild
in TOM when compared to the same issues in languages like C++ or
Eiffel. To sum it up: repeated inheritance is shared inheritance.
- With respect to instance variables, things remain the same: every
instance variable declared in a superclass gets its spot in the
subclass. Thus,
D only has one isa
instance variable (inherited from State) , even though
it `is inherited twice'.
- If a method
foo is defined by State ,
unharmed by B , and redefined by C , it is
the redefinition of C that is applicable to
D . The implementation by State that `is
visible' through the inheritance of B is nulled by it
being overriden in the inheritance path through C .
Messaging super
Messaging super is performing an invocation of a
method as provided by a superclass. Usually, the method is
invoked from the method that overrides the original definition.
For example, the following is not uncommon for an initializer:
|
id
init
{
my_counter = 1;
= [super init];
}
|
If a class has multiple superclasses, the message to super must
indicate which superclass is to provide the method implementation.
If the super message is unambiguous, the compiler will make the
obvious choice, that can be described as follows: Suppose class
D overrides the method init as described
above. Imagine a class E , with exactly the same
superclasses as D , but without overriding any method.
Then, invoking init on an instance of E
will be bound to a particular method implementation. If that
implementation is provided by a direct superclass of
E or is only visible through a single direct
superclass of E , than that is the superclass a
message to super in D refers to. If, however, there
is a method clash, or the method is visible through more than one
direct superclass, the super reference is ambiguous and must be
disambiguated, as shown in this example (note that the syntax of
directing which super to message is different from `casting
super'):
|
id
init
{
my_counter = 1;
= [super (B) init];
}
|
Net effect is that when dynamic loading introduces an
init method for instances of class C ,
that change will not be applicable to this method and its
messaging of super.
That's it for this moment. More interesting bits, such the answer to
the question `Why does TOM not have protocols, interfaces, or
signatures?' in a future highlight.
Up: Highlights
|