Thursday, June 14, 2007

3.4 Inheritance and the Data Member

The actual ordering of the derived and base class parts is left unspecified by the Standard. In practice, the base class members always appear first, except in the case of a virtual base class.

Inheritance without Polymorphism (virtual functions)

What are the possible pitfalls of transforming two independent classes into a type/subtype relationship through inheritance?
1. A naive design might, in face, double the number of function calls to perform the same operations. Choosing candidate functions for inlining is an important, if unglamorous, aspect of class design.
2. A second possible pitfall in factoring a class into a two-level or deeper hierarchy is a possible bloating of the space necessary to represent the abstraction as a class hierarchy. The issue is the language guarantee of the integrity of the base class subobject within the derived class.

Adding Polymorphism (The heart of Object Oriented Programming)

* Introduction of a vritual table associated with the class to hold the address of each virtual function it declares. The size of this table in general is the number of virtual functions declared plus an additional one or two slots to support runtime type identification.
* Introduction of the vptr within each class object. The vptr provides the runtime link for an object to efficiently find its associated virtual table. vptr will also be inherited by the derived class.
* Augmentation of the constructor to initialize the object's vptr to the virtual table of the class. Depending on the aggressive of the compiler's optimization, this may mean resetting the vptr within the derived and each base class constructor.
* Augmentation of the destructor to reset the vptr to the associated virtual table of the class.(The oder of destructor calls is in reverse: derived class and then base class)

Placing the vptr at the start of the class is more efficient in supporting some virtual function invocations through pointers to class members under multiple inheritance.

Multiple Inheritance

For single inheritance, the base and derived class objects both begin at the same address. They differ in that the derived object extends the length of its nonstatic data members.

Placing the vptr at the beginning of the class object breaks the natural polymorphism of single inheritance in the special case of a base class without virtual functions and a derived class with them. The conversion of the derived object to the base in this case requires the intervention of the compiler in order to adjust the address being assigned by the size of the vptr. Under both multiple and virtual inheritances, the need for compiler intervention is considerably more prnounced.

The problem of multiple inheritance primarily affects conversions between the derived and second or subsequent base class objects.

What about access of a data member of a second or subsequent base class? Is there an additional cost?
No. The member's location is fixed at compile time. Hence its access is a simple offset the same as under single inheritance regardless of whether it is a pointer.

Multiple inheritance will inherit multiple vptrs.

Virtual Inheritance

A semantic side effect of multiple inheritance is the need to support a form of shared subobject inheritance. The language support level solution is the introduction of virtual inheritance.

The general implementation solution is as follows. A class containing one or more virtual base class subobjects, is divided into two regions: an invariant region and a shared region. Data within the invariant region remains at a fixed offset from the start of the object regardless of subsequent derivations. So members within the invariant region can be accessed directly. The shared region represents the virtual base class subobjects. The location of data within the shared region fluctuates with each derivation. So members within the shared region need to be accessed indirectly. What has varied among implementations is the method of indirect access.

The general layout sttrategy is to first lay down the invariant region of the derived class and then build up the shared region. How is the implementation to gain access to the shared region of the class?
1. In the original cfront implementation, a pointer to each virtual base class is inserted within each derived class object.
2. Microsoft's compiler introduced the virtual base class table. Each class object with one or more virtual base classes has a pointer to the virtual base class table inserted within it. The actual base class pointers, of course, are placed within the table.
3. Sun's compiler, virtual table offset strategy, the virtual function table is indexed by both positive and negative indices. The positive indices, as previously, index into the set of virtual functions; the negative indices retrieve the virtual base class offsets.

Access of an inherited virtual base class member through a nonpolymorphic class object can be optimized by an implementation into a direct member access, much as a virtual function call through an object can be resolved at compile time.

In general, the most efficient use of a virtual base class is that of an abstract virtual base class with no associated data members.

No comments: