[ Team LiB ] |
![]() ![]() |
Gotcha #73: Overloading Virtual FunctionsWhat's wrong with the following base class fragment? class Thing { public: // . . . virtual void update( int ); virtual void update( double ); }; Consider a derived class produced by a designer who has determined that only the integer version of update requires different behavior in the derived class: class MyThing : public Thing { public: // . . . void update( int ); }; Here we have an unhappy confluence of overloading and overriding—which have nothing to do with each other. The result is similar to that of hiding a base class nonvirtual: the behavior of a MyThing object will vary depending on the interface used to access it:
MyThing *mt = new MyThing;
Thing *t = mt;
t->update( 12.3 ); // OK, base
mt->update( 12.3 ); // oops, derived!
The call mt->update( 12.3 ) will find the name update in the derived class and then perform a successful match by converting the double argument to an int. This is probably not what the programmer intended. Even if it is the intent of a programmer with an unusual worldview, such code does not benefit future maintainers of the code of a more conventional mind-set. Rather than argue against the overloading of virtual functions, we could, as is often suggested in substandard C++ texts, demand that derived class designers override every member of a set of overloaded virtual functions. This approach is impractical, because it requires every derived class designer to follow a particular rule. Many derived classes, such as those used to extend a framework, are developed in environments remote from that of the base class and the coding and design conventions under which it was developed. In any case, avoiding the overloading of virtual functions does not impose any severe restriction on the base class interface. If the overloading is essential to the usability of the base class, it's perfectly reasonable to overload nonvirtual functions that kick down to differently named virtual functions: class Thing { public: // . . . void update( int ); void update( double ); protected: virtual void updateInt( int ); virtual void updateDouble( double ); }; inline void Thing::update( int a ) { updateInt( a ); } inline void Thing::update( double d ) { updateDouble( d ); } The derived class may now override either virtual function independently without defeating polymorphism. Of course, the derived class should not declare a member called update; the ban on hiding base class nonvirtuals is still in effect! This rule has exceptions, but they're relatively uncommon. One exception arises in a common implementation of the Visitor pattern (see Gotcha #77). |
[ Team LiB ] |
![]() ![]() |