In C++, polymorphism "exhibits" itself as the potential addressing of a derived class object through a pointer or reference of a public base class. The presence of at least one virtual function is the criterion for identifying those classes requiring additional runtime information.
ptr -> z(); // z() is a virtual function
What information is needed to invoke the correct runtime instance of the virtual fucntion z()?
1. The actual type of the object addressed by ptr. This allow us to choose the correct instance of z().
2. The location of that instance of z() in order to invoke it.
In C++, the set of virtual functions capable of being invoked through an object of its class is known at compile time. Moreover, this set is invariant. It can NOT be added to nor can a virtual instance be replaced at runtime. The table, therefore, serves only as a passive repository.
An internally generated virtual table pointer is inserted within each class object to find the table. To find the function's address, each virtual function is assigned a fixed index within the table.
The virtual table is generated on a per-class basis. Each table holds the addresses of all the virtual function instances "active" for objects of the table's associated class. These active functions consist of the following:
1. An instance defined within the class, thus overriding a possible base class instance.
2. An instance inherited from the base class, should the derived class choose not to override it.
3. A pure_virtual_called() library instance that serves as both a place holder for a pure virtual function and a runtime exception should the instance somehow be invoked.
Each virtual function is assigned a fixed index in the virtual table. The index remains associated with the particular virtual function throughout the inheritance hierarchy. 每个slot对应的virtual function永远不变。
Three possibilities:
1. It can inherit the instance of the virtual function declared within the base class. Literally, the address of that instance is copied into the associated slot in the derived class's virtual table.
2. It can override the instance of one of its own. In this case, the address of its instance is placed within the associated slot.
3. It can introduce a new virtual function not present in the base class. In this case, the virtual table is grown by a slot and the address of the function is placed within that slot.
ptr->z() will be transformed into (*ptr->vptr[4])(ptr); The only thing we don't know until runtime is the address of which instance of z() is actually contained in slot 4.
Virtual Functions under Multiple Inheritance (难点)
The complexity of virtual function support under multiple inheritance revolves around the second and subsequent base classes and the need to adjust the this pointer at runtime.
The general rule is that the this pointer adjustment of a derived class virtual function invocation through a poniter (or reference) of a second or subsequent base class must be accomplished at runtime. That is, the size of the necessary offset and the code to add it to the this pointer mush be tucked aways somewhere by the compiler. Where?
One efficient solution is the use of a thunk. The thunk is a small assembly stub that a) adjusts the this pointer with the appropriate offset and then b) jumps to the virtual function. In this way, the address placed within each slot either directly addresses the virtual function or addresses an associated think, if an adjustment of the this pointer is necessary.
Under multiple inheritance, a derived class contains n-1 additional virtual tables, where n represents the number of its immediate base classes. (Sun compiler concatenates the multiple virtual tables into one for speedup purpose)
Virtual Functions under Virtual Inheritance
The beginning of the derived class and base class is no longer coincident, so conversion between the two also requires a this pointer adjustment. Stanley recommends: Not to declare nonstatic data members within a virtual base class.
No comments:
Post a Comment