Bug in gcc 2.9x
The JX Application Framework circumvents the bug by using factory methods (Create()). These functions must be called in place of operator new. The code that would trigger the bug is called inside Create() after the constructor returns.
This bug has been fixed in gcc 3.x. The work arounds in the JX Application Framework will be weeded out once gcc 3.x becomes widely used.
The bug appears to mainly affect Linux systems using glibc, because the optimization that causes the problem is supposedly turned off on most other systems, though nobody seems to know exactly which ones. The easiest way to check is to run the test program listed below.
The following code demonstrates a bug in gcc 2.9x when vtable thunks are turned on.
B
/ \
A D - E
\ /
C
In the above inheritance tree, B and C inherit virtually from A. When an object of type E is constructed, D's constructor calls a function in A which in turn calls a virtual function overridden by D. This requires a downcast from type A to type D, and egcs incorrectly adjusts the object pointer, forgetting the size of E's member data.
The correct output, produced by gcc 2.7.2, is:
starting test
A::foo 0xbffffb30 1
B::foo 0xbffffb18 1
C::foo 0xbffffb20 1
D::foo 0xbffffb18 1
E::foo 0xbffffb18 1
object constructed
E::foo 0xbffffb18 1
test finished
gcc 2.96 produces the incorrect output shown in bold:
starting test
A::foo 0xbffffb40 1
B::foo 0xbffffb38 1073783288
C::foo 0xbffffb38 1073783288
D::foo 0xbffffb2c 1073783288
E::foo 0xbffffb28 1
object constructed
E::foo 0xbffffb28 1
test finished
The minimal source code required to reproduce the problem is listed below. Changing the size of E's member data changes the difference between the pointer printed by D::foo() and E::foo() by exactly the same amount.
#include <iostream.h>
class A
{
public:
A() { a=1; bar(); };
virtual void foo() {
cout << "A::foo " << this << ' ' << a << endl; };
void bar() { foo(); };
private:
int a;
};
class B : virtual public A
{
public:
B() { b=1; bar(); };
virtual void foo() {
cout << "B::foo " << this << ' ' << b << endl; };
private:
int b;
};
class C : virtual public A
{
public:
C() { c=1; bar(); };
virtual void foo() {
cout << "C::foo " << this << ' ' << c << endl; };
private:
int c;
};
class D : public B, public C
{
public:
D() { d=1; bar(); };
virtual void foo() {
cout << "D::foo " << this << ' ' << d << endl; };
private:
int d;
};
class E : public D
{
public:
E() { e=1; bar(); };
virtual void foo() {
cout << "E::foo " << this << ' ' << e << endl; };
private:
int e;
};
int
main()
{
cout << "starting test" << endl;
E e;
cout << "object constructed" << endl;
e.bar();
cout << "test finished" << endl;
return 0;
}