Chapter 3 The Application Description Language

3.24 Inheritance

All variable or object members defined in a parent class are accessible in a subclass unless they are hidden by a member of the same name. In such a case, the inherited members are still accessible, provided that you identify them using the scoping operator ParentClassName::memberName. Such an expression is known as a scope pair. If the parent class name is not known, you can use the keyword inherited in place of a class name before the scoping operator to access the nearest occurrence of an otherwise hidden member in the chain of inheritance.
Example of Inheritance and Member Concealment
class Dad
{
XFbutton button;
on DoIt { ... }
};

class Child : Dad
{
XFbutton button;
on Foo
{

string message;
button.width = 40; // OK. Member hides parents
'DoIt=>self; // Dad::DoIt()
inherited::button.width = 100; //Dad::button
}
};

You can invoke a method defined in a parent class by a message to a derived class unless the method has been declared local in the parent class. The exceptions are the system messages Init and Destroy, the system default constructor message Construct, and any other method declared as a constructor by the developer. These methods are local to the class in which you declare them by default, and you may not invoke them by a message to a derived class. The rationale for this is that these messages are sent to objects and bases during initialization and destruction. If these messages could be inherited and were not redefined in each class, they might execute multiple times during the initialization or destruction of an object.

You can redefine or override methods in a derived class. If you want to access the version of a method defined in a base, you can direct the message using the self keyword qualified with the scope operator and the name of the base, provided the message executes in one of the methods of the derived class. There is currently no way to send a message to a base of an arbitrary object although there is a proposal to allow scope pairs with member self in member selection expressions Figure 3.45.
Example of Inheritance and Method Concealment
class A

{

on DoIt { ... }

};

class B : A

{

on DoIt

{

...

'DoIt => A::self;

}

};

B b;

on Init

{

'DoIt => b.A::self; // proposal; not implemented

}

Since a user-defined class can be a subclass of multiple classes, the ADL supports multiple inheritance. Multiple inheritance creates ambiguity when there are methods or members defined with the same name in two separate parent classes of the same subclass. Such ambiguous references in a method of a subclass are an error in C++, but the methods used to detect such conflicts are compute intensive and more appropriate for the compile-time type checking of C++ than the run-time lookup of the ADL.

AM2 searches superclasses in a depth-first manner during inheritance lookup. If this order is not appropriate, the author may use inherited declaration (unimplemented) to indicate in which subclass a method or member should be sought. The keyword inherited introduces such a declaration, followed by a base class name, the scoping operator, the type of the inherited member in the base, and the member name. After such a declaration, the member name refers to the similarly named member in the specified base, even if that member is not the first of that name in inheritance order. Figure 3.46 provides an example.
Multiple Inheritance and Scope Example
class GrandDad

{

on Go { ... }

}

class Dad : GrandDad
{
XFbutton button;
on DoIt { ... }
};

class Mom
{
XFbutton button;
on DoIt { ... }};
on Go { ... }

class Child : Dad, Mom
{
inherited Mom::XFbutton button; // unimplemented
on Foo
{

string message;
button.width = 40; // Uses Mom::button
'DoIt => self; // Uses Dad::DoIt
'Go => self; // Uses GrandDad::Go
}
};

The choice of depth-first search is arbitrary but simple, and has the virtue that it establishes an unambiguous priority among parent classes. In the examples in Figure 3.46, Child is more closely related to Dad and GrandDad than it is to Mom.

The usage of the keyword derived in a scope pair with a member name or with the self keyword parallels the usage of the inherited keyword. The expression derived::self, when it appears in a method of a base of a derived object, refers not to the base but to the whole derived object. Likewise, derived:: prefixed to a member name refers to the member not (necessarily) in the base but rather the first occurrence of the member in inheritance order in the whole derived object. The rationale for the derived keyword is to provide part of the functionality of virtual functions in C++. You can divide this functionality in two parts:

ADL handles provide a freer, though less safe, version of C++'s polymorphism. Indeed, since handles are untyped, they provide total polymorphism. There is no check that an object will understand a message until the message is received at run-time. The polymorphism provided by C++ virtual functions is therefore unnecessary. But communication from base to derived object can be very important, especially in the case of mix-in classes. The derived keyword provides this communication. (For an implementation of the derived keyword, refer to the example program in Figure 4.11, "A Class Inheriting from the ActivityManager Class" page 76.)


AM2 Documentation - 19 NOV 1996

Generated with Harlequin WebMaker