I l@ve RuBoard Previous Section Next Section

Solution

graphics/bulb_icon.gif

In my experience, many C++ programmers still seem to march to the "it isn't OO unless you inherit" battle hymn, by which I mean that they use inheritance more than necessary. See Item 24 for the whole exhausting lecture; the bottom line is simply that inheritance (including, but not limited to, IS-A) is a much stronger relationship than HAS-A or USES-A. When it comes to managing dependencies, therefore, you should always prefer composition/membership over inheritance. To paraphrase Albert Einstein: "Use as strong a relationship as necessary, but no stronger."

In this code, X is derived publicly from A and privately from B. Recall that public inheritance should always model IS-A and satisfy the Liskov Substitution Principle.[2] In this case, X IS-A A and there's naught wrong with it, so we'll leave that as it is.

[2] For lots of good discussion about applying the LSP, see the papers available online at www.gotw.ca/publications/xc++/om.htm, as well as Martin95.

But did you notice the interesting thing about B?

The interesting thing about B is this: B is a private base class of X, but B has no virtual functions. Now, usually, the only reason you would choose private inheritance over composition/membership is to gain access to protected members梬hich most times means "to override a virtual function."[3] As we see, B has no virtual functions, so there's probably no reason to prefer the stronger relationship of inheritance (unless X needs access to some protected function or data in B, of course, but for now I'll assume this is not the case). Assuming that is, indeed, the case, however, instead of having a base subobject of type B, X probably ought to have simply a member object of type B. Therefore, the way to further simplify the header is remove unnecessary inheritance from class B.

[3] Yes, there are other possible reasons to inherit, but the situations where those arise are rare and/or obscure. See Sutter98(a) and Sutter99 for an extensive discussion of the (few) reasons to use inheritance of any kind. Those articles point out in detail why containment/membership should often be used instead of inheritance.



#include "b.h"  // class B (has no virtual functions) 


Because the B member object should be private (it is, after all, an implementation detail), this member should live in X's hidden pimpl_ portion.

Guideline

graphics/guideline_icon.gif

Never inherit when composition is sufficient.


This leaves us with vastly simplified header code.



//  x.h: after removing unnecessary inheritance 


//


#include <iosfwd>


#include "a.h"  // class A


class B;


class C;


class E;


class X : public A


{


public:


     X( const C& );


  B  f( int, char* );


  C  f( int, C );


  C& g( B );


  E  h( E );


  virtual std::ostream& print( std::ostream& ) const;


private:


  struct XImpl;


  XImpl* pimpl_; // this now quietly includes a B


};


inline std::ostream& operator<<( std::ostream& os, const X& x )


{


  return x.print(os);


}


After three passes of progressively greater simplification, the final result is that x.h is still using other class names all over the place, but clients of X need only pay for two #includes: a.h and iosfwd. What an improvement over the original!

    I l@ve RuBoard Previous Section Next Section