I l@ve RuBoard | ![]() ![]() |
Solution![]() 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.
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—which 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.
#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
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 | ![]() ![]() |