name hiding and fragile base problem
Solution 1:
I'll assume that by "fragile base class", you mean a situation where changes to the base class can break code that uses derived classes (that being the definition I found on Wikipedia). I'm not sure what virtual functions have to do with this, but I can explain how hiding helps avoid this problem. Consider the following:
struct A {};
struct B : public A
{
void f(float);
};
void do_stuff()
{
B b;
b.f(3);
}
The function call in do_stuff
calls B::f(float)
.
Now suppose someone modifies the base class, and adds a function void f(int);
. Without hiding, this would be a better match for the function argument in main
; you've either changed the behaviour of do_stuff
(if the new function is public), or caused a compile error (if it's private), without changing either do_stuff
or any of its direct dependencies. With hiding, you haven't changed the behaviour, and such breakage is only possible if you explicitly disable hiding with a using
declaration.
Solution 2:
I don't think that overloads of virtual functions are treated any differently that overloads of regular functions. There might be one side effect though.
Suppose we have a 3 layers hierarchy:
struct Base {};
struct Derived: Base { void foo(int i); };
struct Top: Derived { void foo(int i); }; // hides Derived::foo
When I write:
void bar(Derived& d) { d.foo(3); }
the call is statically resolved to Derived::foo
, whatever the true (runtime) type that d
may have.
However, if I then introduce virtual void foo(int i);
in Base
, then everything changes. Suddenly Derived::foo
and Top::foo
become overrides, instead of mere overload that hid the name in their respective base class.
This means that d.foo(3);
is now resolved statically not directly to a method call, but to a virtual dispatch.
Therefore Top top; bar(top)
will call Top::foo
(via virtual dispatch), where it previously called Derived::foo
.
It might not be desirable. It could be fixed by explicitly qualifying the call d.Derived::foo(3);
, but it sure is an unfortunate side effect.
Of course, it is primarily a design problem. It will only happen if the signature are compatible, else we'll have name hiding, and no override; therefore one could argue that having "potential" overrides for non-virtual functions is inviting troubles anyway (dunno if any warning exist for this, it could warrant one, to prevent being put in such a situation).
Note: if we remove Top, then it is perfectly fine to introduce the new virtual method, since all old calls were already handled by Derived::foo anyway, and thus only new code may be impacted
It is something to keep in mind though when introducing new virtual
methods in a base class, especially when the impacted code is unknown (libraries delivered to clients).
Note that C++0x has the override
attribute to check that a method is truly an override of a base virtual; while it does not solve the immediate problem, in the future we might imagine compilers having a warning for "accidental" overrides (ie, overrides not marked as such) in which case such an issue could be caught at compile-time after the introduction of the virtual method.