SINGLE INHERITANCE OF DATA FIELDS

To evaluate the expression v.position, where v belongs to class Vehicle, the compiler must generate code to fetch the field position from the object (record) that v points to. This seems simple enough: The environment entry for variable v contains (among other things) a pointer to the type (class) description of Vehicle; this has a list of fields and their offsets. But at run time the variable v could also contain a pointer to a Car or Truck; where will the position field be in a Car or Truck object? Single inheritance For single-inheritance languages, in which each class extends just one parent class, the simple technique of prefixing works well. Where B extends A, those fields of B that are inherited from A are laid out in a B record at the beginning, in the same order they appear in A records. Fields of B not inherited from A are placed afterward, as shown in .
Image 14.2: Single inheritance of data fields.

METHODS

A method instance is compiled much like a function: It turns into machine code that resides at a particular address in the instruction space. Let us say, for example, that the method instance Truck_move has an entry point at machine-code label Truck_move. In the semantic analysis phase of the compiler, each variable's environment entry contains a pointer to its class descriptor; each class descriptor contains a pointer to its parent class, and also a list of method instances; and each method instance has a machine-code label. Static methods Some object-oriented languages allow some methods to be declared static. The machine code that executes when c.f() is called depends on the type of the variable c, not the type of the that c holds. To compile a method-call of the form c.f(), the compiler finds the class of c; let us suppose it is class C. Then it searches in class C for a method f; suppose none is found. Then it searches the parent class of C, class B, for a method f; then the parent class of B; and so on. Suppose in some ancestor class A it finds a static method f; then it can compile a function call to label A_f. Dynamic methods This technique will not work for dynamic methods. If method f in A is a dynamic method, then it might be overridden in some class D which is a subclass of C (see ). But there is no way to tell at compile time if the variable c is pointing to an object of class D (in which case D_f should be called) or class C (in which case A_f should be called).
Image 14.3: Class descriptors for dynamic method lookup.

To solve this problem, the class descriptor must contain a vector with a method instance for each (nonstatic) method name. When class B inherits from A, the method table starts with entries for all method names known to A, and then continues with new methods declared by B. This is very much like the arrangement of fields in objects with inheritance. shows what happens when class D overrides method f. Although the entry for f is at the beginning of D's method table, as it is also at the beginning of the ancestor class A's method table, it points to a different method-instance label because f has been overridden. To execute c.f(), where f is a dynamic method, the compiled code must execute these instructions:

  1. Fetch the class descriptor d at offset 0 from object c.
  2. Fetch the method-instance pointer p from the (constant) f offset of d.
  3. Jump to address p, saving return address (that is, call p).