tuck@jason.cs.unc.edu (Russ Tuck) (02/22/90)
The example below shows that g++ invokes constructors for multiply virtually defined objects in the wrong order, and invokes some virtual functions on these objects with bad values of "this". The class structure below is ,-> derived_A -. base -< >-> derived_AB `-> derived_B -' with all derivations "public virtual". Each class has a constructor, and the print statements and c'tor argument values used make it possible to see where and in what order the c'tors are invoked. The first problem is that derived classes are constructed before base classes. (C'tors for derived_A and derived_B are called before c'tor for base, so base data "shared" is not initialized.) The base class declares and defines four virtual functions. Each derived class redefines one of these, and all four are invoked on a derived_AB object. The correct function is called in each case, but the functions redefined in derived_A and derived_B are called with bad values of "this". These functions are passes a "this" value which points to the derived_AB object. As a result, these functions get garbage when they access member data of their own class. This is the second problem. Cfront 2.0 (both directly from AT&T, and from Sun) does not exhibit either problem. The session below is on a Sun-4/280 running SunOS 4.0.3 with g++ 1.36.4 (built on gcc 1.36.93) and Sun's version of AT&T cfront 2.0. (The bug is a real problem for me, because I have no work-around and I'll be in real trouble if I can't get the larger program that shows this bug working soon. Cfront 2.0 has an unrelated bug that I understand less well and have no work around for, so I'm currently without a usable C++ compiler. Even if AT&T fixed cfront tomorrow, they wouldn't release it for months, so GNU is my best hope.) # Here's the example program. (Not preprocessed, because only includes # stdio.h. If you need it preprocessed anyway, tell me and I'll send it.) tuck@jason> cat virt.C /* Russ Tuck, 2/21/90. */ /* Show g++ bug handling virtual functions with multiple virtual inheritance.*/ #include <stdio.h> class base { private: int base_val; protected: int shared; public: virtual base* b(); class derived_A; virtual derived_A* d_A(); class derived_B; virtual derived_B* d_B(); class derived_AB; virtual derived_AB* d_AB(); base(int n=0); }; base::base(int n) { base_val = n; shared=n; printf("base(%d) (this = %lx)\n", n, (long)this); } base* base::b() { printf("base::b() (v=%d) returning %lx\n",base_val,(long)this);return(this);} derived_A* base::d_A() { printf("base::d_A() returning NULL\n"); return(NULL); } derived_B* base::d_B() { printf("base::d_B() returning NULL\n"); return(NULL); } derived_AB* base::d_AB() { printf("base::d_AB() returning NULL\n"); return(NULL); } class derived_A : virtual public base { private: int A_val; public: derived_A* d_A(); derived_A(int n=1); }; derived_A::derived_A(int n) : base(n-1) { A_val = n; printf("derived_A(%d) (shared=%d) (this = %lx)\n", n, shared, (long)this); } derived_A* derived_A::d_A() { printf("base::d_A() (Av=%d) returning %lx\n",A_val,(long)this);return(this);} class derived_B : virtual public base { private: int B_val; public: derived_B* d_B(); derived_B(int n=2); }; derived_B::derived_B(int n) : base(n-2) { B_val = n; printf("derived_B(%d) (shared=%d) (this = %lx)\n", n, shared, (long)this); } derived_B* derived_B::d_B() { printf("base::d_B() (Bv=%d) returning %lx\n",B_val,(long)this);return(this);} class derived_AB : virtual public derived_A, virtual public derived_B { private: int AB_val; public: derived_AB* d_AB(); derived_AB(int n=3); }; derived_AB::derived_AB(int n) : base(n+5), derived_A(n+10), derived_B(n+15) { AB_val = n; printf("derived_AB(%d) (shared=%d) (this = %lx)\n", n, shared, (long)this); } derived_AB* derived_AB::d_AB() { printf("base::d_AB() (ABv=%d) returning %lx\n",AB_val, (long)this); return(this); } main() { derived_AB* AB_ptr; AB_ptr = new derived_AB(5); /* Each should print the same ptr value printed by corresponding c'tor. */ printf("AB_ptr = %lx\n", (long)AB_ptr); printf("AB_ptr->b() = %lx\n", (long)AB_ptr->b()); printf("AB_ptr->d_A() = %lx\n", (long)AB_ptr->d_A()); printf("AB_ptr->d_B() = %lx\n", (long)AB_ptr->d_B()); printf("AB_ptr->d_AB() = %lx\n", (long)AB_ptr->d_AB()); } # Compile and run with G++. tuck@jason> g++ -v virt.C g++ version 1.36.4 (based on GCC 1.36.93) /usr/softlab/contrib/lib/g++-1.36.4/sparc_sunos/gcc-cpp -+ -v -undef -D__GNUC__ -D__GNUG__ -D__cplusplus -Dsparc -Dsun -Dunix -D__sparc__ -D__sun__ -D__unix__ virt.C /usr/tmp/cca18882.cpp GNU CPP version 1.36.93 /usr/softlab/contrib/lib/g++-1.36.4/sparc_sunos/gcc-cc1plus /usr/tmp/cca18882.cpp -quiet -dumpbase virt.C -version -o /usr/tmp/cca18882.s GNU C++ version 1.36.4 (based on GCC 1.36.93) (sparc) compiled by GNU C version 1.36. default target switches: -mfpu -mepilogue as -o virt.o /usr/tmp/cca18882.s /usr/softlab/contrib/lib/g++-1.36.4/sparc_sunos/gcc-ld -e start -dc -dp /lib/crt0.o virt.o -lg++ /usr/softlab/contrib/lib/g++-1.36.4/sparc_sunos/gcc-gnulib -lc tuck@jason> a.out derived_A(15) (shared=0) (this = ebf0) derived_B(20) (shared=0) (this = ec04) base(10) (this = ebe4) derived_AB(5) (shared=10) (this = ebd8) AB_ptr = ebd8 base::b() (v=10) returning ebe4 AB_ptr->b() = ebe4 base::d_A() (Av=60400) returning ebd8 AB_ptr->d_A() = ebd8 base::d_B() (Bv=60400) returning ebd8 AB_ptr->d_B() = ebd8 base::d_AB() (ABv=5) returning ebd8 AB_ptr->d_AB() = ebd8 # Compile and run with cfront 2.0 for comparison. Output is correct. tuck@jason> CC -v virt.C Sun C++ 2.0 FCS - 10/20/89 /usr/CC/sun4//cpp -C -B -Dc_plusplus=1 -D__cplusplus=1 -I/usr/CC/sun4/incl virt.C >./C++.18888/cpptmp /usr/CC/sun4/cfront +L +fvirt.C <./C++.18888/cpptmp >./C++.18888/virt.c cc -I/usr/CC/sun4/incl virt.c -L/usr/CC/sun4/ -lC # static constructors/destructors nm a.out | /usr/CC/sun4/munch > ./C++.18888/__ctdt18888.c tuck@jason> a.out base(10) (this = 4420) derived_A(15) (shared=10) (this = 442c) derived_B(20) (shared=10) (this = 4414) derived_AB(5) (shared=10) (this = 4400) AB_ptr = 4400 base::b() (v=10) returning 4420 AB_ptr->b() = 4420 base::d_A() (Av=15) returning 442c AB_ptr->d_A() = 442c base::d_B() (Bv=20) returning 4414 AB_ptr->d_B() = 4414 base::d_AB() (ABv=5) returning 4400 AB_ptr->d_AB() = 4400 tuck@jason> Russ Tuck tuck@cs.unc.edu UNC Dept. of Computer Science ...!mcnc!unc!tuck CB 3175, Sitterson Hall Chapel Hill, NC 27599-3175, USA (919) 962-1755 or 962-1700