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!kurtlshopiro@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