asc@concurrent.co.uk (Andy Chittenden) (01/24/90)
// The following program demonstrates a problem with virtual inheritence:
class base {
public:
base();
base(unsigned long& p);
};
class derived : public virtual base {
public:
derived();
derived(unsigned long& p);
};
derived::derived(unsigned long& p) : base(p) {}
class mostderived : public virtual derived {
public:
mostderived();
mostderived(unsigned long& p);
};
mostderived::mostderived(unsigned long& p) : derived(p) {}
main()
{
unsigned long counter;
mostderived x(counter);
}
// The problem encountered is that without virtual inheritence the
// constructor for mostderived with parameter p does call derived's
// constructor with parameter p and derived's constructor with parameter p
// does call upon base's constructor with parameter. This is exactly how
// you would expect it to work. However, with virtual inheritence, it
// doesn't work like this at all. In order to prevent the constructor for
// base being called twice when used with multiple inheritence,
// mostderived's constructor with parameter p calls upon base's constructor
// WITHOUT parameter p. This means that the base part of mostderived is
// not constructed properly. The code generated is ( with much
// editting):
// struct mostderived *__ct__11mostderivedFRUl (__0this ,
// __0base , __0derived , __0p )
// struct mostderived *__0this ;
// struct base *__0base ;
// struct derived *__0derived ;
// unsigned long *__0p ;
// #line 25 "0708067014.C"
// { if (__0this ||
// (__0this = (struct mostderived *)__nw__FUi (sizeof(struct mostderived))))
// ( (__0this -> Pbase= ((__0base == 0 )?( (__0base =
// #line 25 "0708067014.C"
// (((struct base *)((((char *)__0this ))+ 12)))),
//->>>>>> __ct__4baseFv ( ((struct base *)((((char *)__0this ))+ 12))) )
// :__0base )), (__0this -> Pderived= ((__0derived ==
// #line 25 "0708067014.C"
// 0 )?( (__0derived = (((struct derived *)((((char *)__0this ))+ 8)))),
// __ct__7derivedFRUl ( ((struct derived *)((((char *)__0this ))+ 8)),
// __0base , __0p ) )
// #line 25 "0708067014.C"
// :__0derived ))) ;
//
// #line 25 "0708067014.C"
// return __0this ;
// }
// The line marked with ->>>>>> is the line that calls upon base's no
// parameter constructor.
// One way around this is to implement the constructor differently as:
// mostderived::mostderived(unsigned long& p) : derived(p), base(p) {}
// However, this presents a problem with regard to impact of change. If
// I now split base into two for some reason:
//
// class mostbase {
// public:
// mostbase();
// mostbase(unsigned long& p);
// };
// and
// class base : public virtual mostbase {
// ....
// };
//
// I would now need to change the implementation of mostderived
// constructor to:
// mostderived::mostderived(unsigned long& p) : derived(p),
// base(p),
// mostbase(p) {}
// I would also need to change derived's constructor as well.
// This seems to go against the object-oriented principle of keeping
// the impact of a change to the class being changed if the interface
// remains the same.
// Cfront could have generated pointers to pointers in the above code
// to prevent base being constructed with the "wrong" constructor. In
// other words (in simplified pseudo C++):
// mostderived::mostderived(mostderived* this, base** x, derived** y,
// unsigned long& p)
// { if (this != NULL || this = new mostderived) {
// *y = (*y == NULL) ? derived::derived((char*) this + 8, x, p)
// : *y ;
// *x = (*x == NULL) ? base::base((char*) this + 12) : *x;
// /* the constructor on this line won't get
// called if derived's constructor
// runs base's constructor */
// this->Pbase = *x;
// this->Pderived = *y;
// return this;
// }
// (Aside: the above code is identical to the AT&T generated code up
// above except that the parameters to the routines are pointers to
// pointers rather than pointers - obviously the *y and *x
// expressions would be y and x respectively in AT&T's generated code.
// This should help your understanding of some AT&T's generated code!).
// Any comments?, Andy