FAQ 29.03 How should the hiding rule be handled?

graphics/new_icon.gif

Avoid triggering the hiding rule when possible, and use the following work-arounds when the hiding rule can't be avoided.

Avoid hiding inherited public: member functions whenever possible. When it cannot be avoided, it is important not to surprise the class's users. The guiding principle is to avoid confusing users: when a Base* can be used to call a member function on a Derived object, calling it via a Derived* shouldn't alter the observable behavior.

In the case of redefining a nonvirtual member function, as in Base::h(float) from the previous FAQ, the simplest way to avoid surprising users is to use the virtual keyword when declaring the base class member function. In those rare cases where the base class function cannot be virtual, ensure that the observable behavior of the derived class function is identical to that of the base class.

For example, an experienced C++ programmer might use a nonvirtual member function to avoid the (small) overhead of a virtual function call, yet might also redefine that member function in a derived class to make better use of the derived class's resources. To avoid surprising users, there must not be any differences in the observable behavior of the two functions. Note: These relationships are somewhat subtle; if the code will be maintained by less experienced programmers, a normal, virtual function is probably a better choice.

In the case where a base class and a derived class declare member functions with the same name but different signatures, as in Base::g(float) and Derived::g(int) in the previous FAQ, a using declaration (FAQ 29.04) should be employed.

The following shows how these guidelines can be applied to the example from the previous FAQ.

 #include <iostream> using namespace std; class Base { public:   virtual ~Base()         throw();   virtual void f(float x) throw();   virtual void g(float x) throw();   virtual void h(float x) throw(); }; Base::~Base() throw()   { } void Base::f(float x) throw()   { cout << "Base::f(float)\n"; } void Base::g(float x) throw()   { cout << "Base::g(float)\n"; } void Base::h(float x) throw()   { cout << "Base::h(float)\n"; } class Derived : public Base { public:   virtual void f(float x) throw();   virtual void g(int x)   throw();                   <-- 1   using Base::g;                                     <-- 2 }; void Derived::f(float x) throw()   { cout << "Derived::f(float)\n"; } void Derived::g(int x) throw()   { cout << "Derived::g(int)\n"; } 

(1) Normally this would hide g(float) (bad!)

(2) But this line unhides g(float) (good!)

After applying these fixes, users are not confused because the behavior depends on the type of the object rather than on the type of the pointer used to access that object.

 void sample(Base& b, Derived& d) {   b.f(3.14f);   d.f(3.14f);   b.g(3.14f);   d.g(3.14f);                                        <-- 1   b.h(3.14f);   d.h(3.14f); } int main() {   Derived d;   sample(d, d); } 

(1) This is not hidden (good!)

The output of this program demonstrates that the behavior depends on the type of the object, not the type of the reference:

 Derived::f(float) Derived::f(float) Derived::g(float) Derived::g(float) Derived::h(float) Derived::h(float) 

These guidelines apply only to public inheritance; hiding base class member functions is fine for private or protected inheritance (see FAQ 37.01).



C++ FAQs
C Programming FAQs: Frequently Asked Questions
ISBN: 0201845199
EAN: 2147483647
Year: 2005
Pages: 566
Authors: Steve Summit

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net