Path: utzoo!attcan!utgpu!jarvis.csri.toronto.edu!mailrus!uwm.edu!gem.mps.ohio-state.edu!apple!bionet!ames!amdahl!fai!kurtl From: kurtl@fai.UUCP (Kurt Luoto) Newsgroups: comp.lang.c++ Subject: MI and virtual functions Keywords: multiple inheritance, virtual functions, ambiguity Message-ID: <2534@fai.UUCP> Date: 26 Sep 89 15:16:53 GMT Reply-To: kurtl@fai.fai.com (Kurt Luoto) Organization: Fujitsu America, Inc Lines: 159 I posted this to the gnu.g++ group, but didn't get any response. I am assuming that for some reason it didn't get through, or perhaps I need to reach the more general audience of comp.lang.c++. I apologize for any redundancy. I have been reading Lippman's "C++ Primer" to familiarize myself with the features of C++ 2.0. On page 312, in discussing function name ambiguities that can arise from multiple inheritance, he gives an example and states: "When the inherited members that share the same name are all member functions, it is therefore a good design strategy to define a derived class member function of the same name." I wanted to try this out myself and see how it worked with virtual functions. I do not yet have AT&T C++ 2.0 available to me, but I do have GNU C++. Here follows my test program, a minor variation, and their respective outputs. I found the results unintuitive. I would like to know whether this is a feature, bug, undefined area of the language, or whatever. --------------------- EXAMPLE ------------------------- % g++ -v g++ version 1.35.1- % % cat test1.C #includeclass A { int a ; public: A(); virtual int foo(); }; A::A() { a = 0 ; } int A::foo() { ++a; cout << "A::foo() : a = " << a << "\n" ; return a ; } class B { int b ; public: B(); virtual int foo(); }; B::B() { b = 0 ; } int B::foo() { ++b; cout << "B::foo() : b = " << b << "\n" ; return b ; } class C : public A, public B { int c ; public: C(); int foo(); }; C::C() { c = 0 ; } int C::foo() { A::foo(); B::foo(); ++c; cout << "C::foo() : c = " << c << "\n" ; return c ; } int garpA( A* a ) { cout << "garpA:\n" ; return a->foo(); } int garpB( B* b ) { cout << "garpB:\n" ; return b->foo(); } int garpC( C* c ) { cout << "garpC:\n" ; return c->foo(); } main( ) { C c ; garpA( &c ); garpB( &c ); garpC( &c ); exit(0); } % % g++ -o test1 test1.C % test1 garpA: A::foo() : a = 1 B::foo() : b = 1 C::foo() : c = 1 garpB: B::foo() : b = 2 garpC: A::foo() : a = 2 B::foo() : b = 3 C::foo() : c = 2 % % diff test1.C test2.C 34c34 < class C : public A, public B { --- > class C : public B, public A { % % g++ -o test2 test2.C % test2 garpA: A::foo() : a = 1 garpB: A::foo() : a = 2 B::foo() : b = 1 C::foo() : c = 1 garpC: A::foo() : a = 3 B::foo() : b = 2 C::foo() : c = 2 --------------------- END OF EXAMPLE ------------------------- I can see by the output how the compiler implementation behaves when the order of base classes is changed for the derived class C. To me it is rather non-intuitive that the results should differ based simply on this ordering. Is this a feature of 2.0? Does the (hypothetical) language standard say that behaviour in this case is undefined? Lippman's book gives no indication that I can see. Does the AT&T 2.0 compiler work the same way as GNU C++? Behaviour that I would have expected would be one of the following: 1. The compiler would flag that the virtual function C::foo() is not allowable and produce a compile time error. 2. The compiler would give a warning message that accessing foo() via different base-class type pointers (A*, B*, C*) to an instance of C may produce unexpected results. 3. The compiler would create virtual functions and tables for both the A and B parts of C such that accessing foo() through any type of pointer to a C instance (A*, B*, or C*) would all produce the same behaviour. Could someone please enlighten me? I apologize if this question has already been thrashed out before. ----------------- Kurt W. Luoto I'm unsure of my mailer. Try kurtl@fai.fai.com or ...!sun!fai!kurtl