multiple inheritance - bguoosd132/wiki.files/lecture10.pdf · a common analogy in describing...
TRANSCRIPT
ברנבוים לאוניד
המחלקה למדעי המחשב
גוריון -אוניברסיטת בן
Multiple Inheritance
Roadmap
In this chapter we will investigate some of the
logical problems that can arise when a language
allows a child class to have multiple parents:
�Name ambiguity
�Impact on substitution
�The Problem of Common Ancestors
�Implementing multiple inheritance in Java
22
Inheritance as Categorization
33
Inheritance as Categorization
� In one sense, the process of inheritance is a
form of categorization
� A TextWindow is a type of Window, so class
TextWindow inherits from class Window
� But in the real world, most objects can be
categorized in a variety of ways
44
Inheritance as Combination
� Instead, real world objects are combinations of features from different classification schemes
� Each category giving some new insight into the whole:
Moshe is Male
Moshe is Israeli
Moshe is a Parent
Moshe is a Professor
� Note that we have not lost the is-a relationship; it still applies in each case
55
Inheritance as Combination
66
Male Professor
Parent
Israeli
Is-a, Has-a, and As-a
77
� A common analogy in describing multiple inheritance is that of an actor playing different roles
� Often at the same time
� Likewise, multiple inheritance is not has-a and not quite the is-a relationship (because the parent class is no longer unique)
� But a variation of the is-a that could be described as as-a
� Rule of thumb:
use multiple inheritance if the “A can be viewed as-a B” sounds right
CS Example - Complex Numbers
Two abstract classifications:
� Magnitude - things that can be compared
� Number - things that can perform arithmetic
Three specific classes:
� Integer - comparable and arithmetic
� Char - comparable but not arithmetic
� Complex - arithmetic but not comparable
88
Possible Solutions
1. Make Number subclass of Magnitude, but redefine comparison
operators in class Complex to give error message if used
(subclassing for limitation)
2. Don't use inheritance at all - redefine all operators in all classes
(flattening the inheritance tree)
3. Use part inheritance, but simulate others - use Number, but have
each number implement all relational operators (partial
flattening)
4. Make Number and Magnitude independent, and have Integer
inherit from both (multiple inheritance)
99
Inheritance as a form of
Combination
1010
Magnitude
Char
NumberInteger
Complex
This reflects the as-a relationship in a natural way
Another Example - Walking Menus
� A Menu is a structure charged with displaying itself
when selected by the user
� A Menumaintains a collection of MenuItems
� Each MenuItem knows how to respond when
selected
� A CascadingMenu is both
a MenuItem and a Menu
1111
Problem with Multiple Inheritance
- Name Ambiguity
What happens when the same name is used in both parent
classes:
�A CardDeck knows how to draw a Card
�A GraphicalItem knows how to draw an Image on a screen
�A GraphicalCardDeck should be able to draw
�Which?
1212
CardDeck
draw()
GraphicalObject
draw()
GraphicalCardDeck
?
Solution A
1313
Explicit disambiguation:
This solution is less than ideal, since the syntax is
different from other function calls,
and programmers must remember which method
comes from which parent
GraphicalCardDeck gcd;
gcd.CardDeck::draw();
gcd.GraphicalObject::draw();
Solution B1414
With different type signatures, can use straight overloading:
If the type signature are similar, or if overloading is not allowed, then the programmer is faced a more difficult decision.
class GCD : public CardDeck, public GraphicalObject {
public:
virtual Card *draw(){
return CardDeck::draw();
};
virtual void draw(GraphicsContext *g){
GraphicalObject::draw(g);
};
};
Solution C
Redefine one or the other operation in the child
class:
1515
class GraphicalCardDeck : public CardDeck,
public GraphicalObject {
public:
virtual void draw () {
return CardDeck::draw();
}
virtual void paint () {
GraphicalObject::draw();
}
}
GraphicalCardDeck gcd;
gcd->draw(); // selects CardDeck draw
gcd->paint(); // selects GraphicalObject draw
Problem with
Redefinition Solution
� The redefinition solution is not without cost
� What happens when we run up against the
principle of substitution?
� This problem can be mitigated, but the solution
is complex and not perfect (see next slide)
1616
GraphicalObject *g = new GraphicalCardDeck();
g->draw(); // opps, doing wrong method!
1717
class GraphicalCardDeck: public CardDeckParent, GraphicalObjectParent {
public:
virtual void cardDeckDraw() {…}
virtual void goDraw() {…}
}
class CardDeckParent : public CardDeck {
public:
virtual void draw() {cardDeckDraw();}
virtual void cardDeckDraw() {CardDeck::draw()}
}
class GraphicalObjectParent : public GraphicalObject {
public:
virtual void draw() {goDraw();}
virtual void goDraw() {GraphicalObject::draw()}
}
class CardDeckParent : public CardDeck {
public:
virtual void draw() {cardDeckDraw();}
virtual void cardDeckDraw() {CardDeck::draw()}
}
class GraphicalObjectParent : public GraphicalObject {
public:
virtual void draw() {goDraw();}
virtual void goDraw() {GraphicalObject::draw()}
}
// All three variables refer to the same object
GraphicalCardDeck *gcd = new GraphicalCardDeck();
CardDeck *cd = gcd;
GraphicalObject *go=gcd;
cd->draw(); // ok, will execute cardDeckDraw
go->draw(); // ok, will execute goDraw
gcd->draw(); // compiler error, ambiguous invocation
// All three variables refer to the same object
GraphicalCardDeck *gcd = new GraphicalCardDeck();
CardDeck *cd = gcd;
GraphicalObject *go=gcd;
cd->draw(); // ok, will execute cardDeckDraw
go->draw(); // ok, will execute goDraw
gcd->draw(); // compiler error, ambiguous invocation
A (complicated?) solution
1818
class GraphicalCardDeck : CardDeck, GraphicalObject {
void CardDeck.draw() { … }
void GraphicalObject.draw() {…}
public void draw() { … }
}
class GraphicalCardDeck : CardDeck, GraphicalObject {
void CardDeck.draw() { … }
void GraphicalObject.draw() {…}
public void draw() { … }
}
A solution in C#
GraphicalCardDeck theDeck = new GraphicalCardDeck();
theDeck.draw(); // Executes method #3
CardDeck cd = (CardDeck) theDeck;
cd.draw(); // Executes method #1
GraphicalObject go = (GraphicalObject) theDeck;
go.draw(); // Executes method #2
GraphicalCardDeck theDeck = new GraphicalCardDeck();
theDeck.draw(); // Executes method #3
CardDeck cd = (CardDeck) theDeck;
cd.draw(); // Executes method #1
GraphicalObject go = (GraphicalObject) theDeck;
go.draw(); // Executes method #2
Other Approaches to
Name Ambiguity
Other languages use different approaches to solving the problem of ambiguous names:
�Eiffel uses the ability to rename features from the parent class
A polymorphic variable accessing through the parents name will access the renamed feature in the child (see next slide)
�LISP and Python resolve ambiguous names by the order in which the parent classes are listed
The first occurrence of the name found in a systematic search is the one selected
1919
Methods redefinition in Eiffel
2020
Eiffel allows to directly redefine a method in the sub-class by
renaming it in the inherit clause:
�The solution is roughly equivalent to helper renaming (having a
specific parent for each inherited copy)
�But more efficient and does not require extra classes
class GraphicalCardDeck
inherit
CardDeck
rename
draw as cardDeckDraw
end
GraphicalObject
rename
draw as goDraw
end
Multiple Inheritance of Interfaces
Multiple inheritance of interfaces does not present the same problem of
name ambiguity as does multiple inheritance of classes:
Either the ambiguous methods in the parent classes have different type signatures,
in which case there is no problem, or
The ambiguous methods in the parent classes have the same signature
Still no problem, since what is inherited is only a specification, not an
implementation
This is why Java permits multiple inheritance of interfaces, not of classes
Nevertheless, C# does not permit the same method name to be inherited
from two parent interfaces.
2121
Inheritance from
Common Ancestors
2222
AA
CC
DD
BB
Does the new object have one or two instances of the ancestor?
AA
CC
DD
BB
AA
Sometimes: one instance
of the ancestor
2323
Sometimes: two instances
of the ancestor
2424
class Link{
public: Link *nextLink;
}
class CardDeck : public Link { … };
class GraphicalObject : public Link{ … };
Presumably, the lists of card decks and graphical objects are distinct, and hence each type of list should have its own links
Data Field in Common Ancestor
� Imagine that the common ancestor declares a data member
� Should the child class have one copy of this data field, or two?
� Both answers can be justified with examples
� C++ gets around this by introducing the idea of a virtual parent class:
� If your parent is virtual there is one copy, and if not there are two
� Not a perfect solution, and makes the language complicated
2525
Virtual InheritanceVirtual InheritanceVirtual InheritanceVirtual Inheritance
2626
� The ambiguity occurs in InOutStream,
� But the solution involves the definitions of InStreamand OutStream
class Stream {...};
class InStream: public virtual Stream {...};
class OutStream: public virtual Stream {...};
class InOutStream:
public InStream, public OutStream {...};
Inner Classes
The ability to nest classes in C++ and Java provides a mechanism
that is nearly equivalent to multiple inheritance, without the
semantic problems:
Within the inner class you can access methods from both parent
classes
This idiom is used a lot in Java. Solves many problems that would
otherwise be addressed using multiple inheritance
Not exactly equivalent to multiple inheritance, but very close
2727
class Child extends ParentOne {
...
class InnerChild extends ParentTwo {
...
// can access both parents
}
}
DelegationSimulating Multiple Inheritance with Interfaces
2828
� Using interfaces and fields can simulate multiple inheritance.
� For example, we would like class C to extend both A and B
� Impossible multiple inheritance in Java:
class A {
public void foo() {...}
}
class B {
public void goo() {...}
}
class C extends A, B { // Not allowed in Java!
}
DelegationAn alternative mechanism for “extending” classes
2929
To make class C “extend" B:
1)Define an interface BInf that includes
all the public methods of B
2)Make classes B and C implement BInf
3)Add a field of type B to class C
4)Implement BInf in C by delegating all
the messages to the new field
To make class C “extend" B:
1)Define an interface BInf that includes
all the public methods of B
2)Make classes B and C implement BInf
3)Add a field of type B to class C
4)Implement BInf in C by delegating all
the messages to the new field
DelegationA Delegation example in Java
3030
class A {
public void foo() {...}
}
interface BInf {
void goo();
}
class B implements BInf {
public void goo {...}
}
class C extneds A implements BInf
{
private B _b;
public void goo() {
_b.goo();
}
}
Chapter Summary
In this chapter we have explored some of the
problems that arise of the concept of multiple
inheritance:
� Name ambiguity
� Impact on substitution
� The problem of common ancestors
� Implementing multiple inheritance in Java
3131