The Fragile Base Class Problem
Resolution is the process of replacing names by relative
addresses and offsets. Normally resolution occurs during
compilation and linking, which occurs before loading and
execution.
This creates a special problem in C++ called the Fragile
Base Class Problem (also known as the Fragile Super Class
Problem and the Constant Recompilation Problem). For
example, assume the following class is declared and compiled:
class Employee {
private:
char *name;
int ssn;
double salary;
public:
// etc.
}
Later a derived class is declared and compiled:
class Secretary: public Employee {
private:
int wpm; // typing speed in words/minute
public:
void display() { cout << wpm; }
// etc.
}
According to the rules of object layout dictated by the C++
compiler, a Secretary object is an Employee object extended
by an integer variable (i.e. wpm). Hence the occurence of
wpm inside display() will be bound to the relative address:
this + offset(wpm)
where
offset(wpm) = sizeof(Employee) + a little bit
This binding will occur at link time if Employee and Secretary
are contained in seperate files.
Now assume the Employee class is modified to contain an extra
field:
class Employee {
private:
char *name;
int ssn;
double salary;
int tenure; // = # years on the job
public:
// etc.
}
Since sizeof(Employee) has changed, the Secretary::display()
method contains the wrong offset for wpm, hence must be
recompiled.
Java avoids this problem by reversing the usual "link then load"
order. This is possible because the byte code produced by a
Java compiler may contain unresolved names (i.e. symbolic
references).
The corresponding Java declarations are:
class Employee {
private String name;
private int ssn;
private double salary;
// etc.
}
and
class Secretary extends Employee {
private int wpm;
public void display(System.out.println(wpm); }
// etc.
}
First the class Secretary is loaded into memory by a class
loader object. This causes the Employee class to be loaded.
Next the Secretary class is linked. This is a four step
process:
1. verify opcodes, jump targets, type consistencies, etc.
2. allocate memory for static variables
3. resolve symbolic references (or put it off until later!)
4. initialize static variables
Finally, main() is called.
Now suppose the Employee class is redefined:
class Employee {
private String name;
private int ssn;
private double salary;
private int tenure; // new field
// etc.
}
Again the old Secretary class is loaded along with the new
Employee class. Following the load phase, the link phase
resolves all symbolic references. This includes replacing
the occrence of the name wpm inside display() with an offset
which (may) depend on sizeof(Employee), but since this
occurs AFTER the new Employee class has been loaded, the
correct offset is computed, hence there is no need to
recompile the Secretary class!
Two final observations:
1. Resolution may be postponed until instance creation time,
or even until execution time.
2. Unlike the C++ compiler, the Java compiler doesn't determine
object layout. (This is determined by the loader.) Hence
the offset of wpm does not necessarily depend on sizeof(Employee)
in Java.
See
http://java.sun.com/doc/language_environment for more info.