Path: utzoo!utgpu!news-server.csri.toronto.edu!bonnie.concordia.ca!uunet!microsoft!jimad From: jimad@microsoft.UUCP (Jim ADCOCK) Newsgroups: comp.lang.c++ Subject: Re: Virtual function problem. Keywords: C++ virtual function Message-ID: <72253@microsoft.UUCP> Date: 9 May 91 19:12:24 GMT References: <3472@trlluna.trl.oz> Reply-To: jimad@microsoft.UUCP (Jim ADCOCK) Organization: Microsoft Corp., Redmond WA Lines: 127 In article <3472@trlluna.trl.oz> mcf@tardis.trl.OZ.AU (Michael Flower) writes: |I have come across something that appears strange. | |class A { | virtual void fn(char); // 1 | virtual void fn(char *); // 2 |}; | |class B : public A { | void fn(int); |}; | |main() {} | |in the following way, I get. | |"t.c", line 7: warning: B::fn() hides virtual A::fn() |"t.c", line 7: warning: B::fn() hides virtual A::fn() | |Of course B::fn() hides A::fn(). That is why I wrote it! After all A::fn() is |virtual. | |If I change the order of lines 1 and 2 (in class A), the problem goes away. |Admittedly it is only a warning, but I don't like warnings, they often spell |trouble. Is this an anomally? I think a C++ compiler *ought* to emit a warning when someone overrides a virtual function with a non-virtual function in a derived class with a public base. Do you realize that B::fn(int) is non-virtual? Maybe the warning ought to be: warning: B::fn() only *partially* hides virtual A::fn() [The order you declare things in A shouldn't matter in this issue -- if a compiler is going to issue a warning it should apply no matter what the ordering of declarations in class A.] Here's why I think you deserve [at least] a warning for what you've done: In class A you've declared a virtual fn(char) that you're stating is part of a polymorphic interface. When you've publicly inherited A in class B, you're stating a B can be used as either an A or a B. If something is polymorphic we expect its behavior to be the same when either accessed as a B or an A: B* bp = new B; A* ap = bp; bp->fn(5); ap->fn(5); And we'd expect the same behavior of fn(5) in either case -- because fn(5) *appears* to be a virtual function, by virtue of fn(?) being declared virtual in the base class. And bp and ap are just alternate names for the same object -- the object should interpret fn(5) message the same in either case. But in reality, the above is resolved as: bp->B::fn(5); // B's non-virtual fn(int) ap->A::fn(5); // A's virtual fn(char) so that the behavior of the B object depends on the type of pointer thru which fn(?) is invoked -- in spite of the fact that in A all flavors of fn(?) are declared virtual. One hardly expects this from a "polymorphic" class, so I think the warning is deserved. Consider the plight, for example, of a C++ programmer coming from the C world who hasn't complete absorbed the notion that char and int really are really distinct in C++, so that they accidentally declare B::fn(int) in the derived class, when they really intended to override the virtual method A::fn(char). Without the warning [or maybe even with it] they might spend a long, long time figuring out why they keep getting A::fn(char) when invoked as ap->fn(5) -- or alternatively why they keep getting B::fn(int) when invoked as bp->fn('A') !!! If in turn a class C is derived from class B, and some of the fn(?) are virtually/non-virtually overridden in C, then the situation becomes even worse. One proposal before the ANSI C++ committee would allow the use of ~virtual to allow a programmer to explicitly state that a method was intended to be non-virtual. In which case in B you could state: ~virtual fn(int); // Don't you warn me, I "know" what I'm doing! ---- I'd recommend that programmers not use functions overloaded by argument with polymorphic classes -- effectively you're doing double dispatch, but with the object type resolved dynamically, while the parameter types resolve statically, which leads to some pretty wierd combinations. But, if you insist on doing this, I think they better all be virtual, all be declared in the same base class, and all be re-implemented in any base class that overrides any one of them. ---- A simplified example of the problem: extern "C" printf(const char*, ...); class A { public: virtual void print(char c) { printf("as A prints(%c)\n", (int)c); } }; class B : public A { public: void print(int i) { printf("as B prints(%d)\n", i); } }; main() { B b; B* bp = &b; A* ap = &b; ap->print('A'); bp->print('A'); bp->print(11); ap->print(11); return 0; }