kurtl@fai.UUCP (Kurt Luoto) (09/26/89)
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 #include <stream.h> class 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
shopiro@alice.UUCP (Jonathan Shopiro) (09/28/89)
In article <2534@fai.UUCP>, kurtl@fai.UUCP (Kurt Luoto) writes: > 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." In fact, if they are virtual functions, you _must_ declare a function by that name in the derived class. > > 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++. Reversing the order of the base classes has no effect on the output of your program in AT&T C++ 2.0. As a simple example of how the language should behave: struct A { virtual void foo(); }; struct B { virtual void foo(); }; struct C : A, B { virtual void foo(); }; void bar() { C c; A* ap = &c; B* bp = &c; C* cp = &c; // these calls all do the same thing ap->foo(); // calls C::foo() bp->foo(); // calls C::foo() cp->foo(); // calls C::foo() } -- Jonathan Shopiro AT&T Bell Laboratories, Warren, NJ 07060-0908 research!shopiro (201) 580-4229