piero@bnrmtl.bnr.ca (Piero Colagrosso) (06/19/91)
I have been experiencing a problem with the use of multiple
inheritance in C++. I am using the Sun (AT&T) compiler on a Sun
Sparc II running SunOS 4.1.1. The problem manifests itself with
both the 2.0 and 2.1 compilers.
Consider the following class hierarchy:
A
/ \
B C
\ /
D1
|
D2
The problem seems to occur when you derive from a class (i.e. D1 in
the example above) which is multiply derived from two other classes
(i.e. B and C) and the following conditions hold:
1) B and C are derived virtually from their parent class (A).
2) A and C implement a virtual function m1.
3) A, D1, and D2 implement a virtual function m2.
The problem is the following:
1) You create a D2 object and assign it to an A* pointer.
2) You subsequently invoke the m1 member function on this object
via the A* pointer.
The result: It seems that the calculation of the "this" pointer
which is passed to m1 (i.e. C::m1) is done incorrectly, resulting in
defective code (an unexpected crash was the orginal symptom
which led to the identification of this problem).
The following example illustrates inconsistent values of C's "this"
pointer in the constructor and subsequent invocation of m1 (see
program output):
--------------------------------------------------------------------
class A
{
public:
A();
virtual void m1();
virtual void m2();
private:
int a;
};
class B : public virtual A
{
public:
B();
virtual void m2();
private:
int b;
};
class C : public virtual A
{
public:
C();
virtual void m1();
private:
int c;
};
class D1: public B, public C
{
public:
D1();
virtual void m2();
private:
int d1;
};
class D2: public D1
{
public:
D2();
virtual void m2();
private:
int d2;
};
#include <iostream.h>
main()
{
A* pA = new D2;
pA->m1();
}
A::A()
{
cerr << "A::A; this = " << this << endl;
}
void A::m1()
{
cerr << "A::m1; this = " << this << endl;
}
void A::m2()
{
cerr << "A::m2; this = " << this << endl;
}
B::B()
{
cerr << "B::B; this = " << this << endl;
}
void B::m2()
{
cerr << "B::m2; this = " << this << endl;
}
C::C()
{
cerr << "C::C; this = " << this << endl;
}
void C::m1()
{
cerr << "C::m1; this = " << this << endl;
}
D1::D1()
{
cerr << "D1::D1; this = " << this << endl;
}
void D1::m2()
{
cerr << "D1::m2; this = " << this << endl;
}
D2::D2()
{
cerr << "D2::D2; this = " << this << endl;
}
void D2::m2()
{
cerr << "D2::m2; this = " << this << endl;
}
--------------------------------------------------------------------
The output generated from this program:
A::A; this = 0x6490
B::B; this = 0x6468
C::C; this = 0x6470 <-------
D1::D1; this = 0x6468 | These aren't the same !!
D2::D2; this = 0x6468 |
C::m1; this = 0x6468 <-------
Am I doing anything wrong or is this really a compiler problem ?
I have noticed that the problem does not occur if:
i) Either D1 or D2 (or both) does not implement the virtual function
m1.
ii) There is no extra level of inheritance after the multiple
inheritance, i.e. if the above class hierarchy stops at D1,
so that we instantiate a D1 object instead of a D2 object.
Based on my understanding of the implementation of MI, as
described in the ARM, I think that the "this" pointer offset is
not being set properly in D2's virtual table (vtbl).
If this really is a compiler problem, is it a known one and are
there any workarounds ?
#######################################################
# Piero Colagrosso Dept. 6s52 #
# Bell-Northern Research, Montreal, Canada. #
# #
# INTERNET: bnrmtl!piero@Larry.McRCIM.McGill.EDU #
#######################################################