Item 28. Minimizing Compile-time Dependencies part 3
来源:互联网 发布:有深意的句子知乎 编辑:程序博客网 时间:2024/05/22 16:38
Item 28. Minimizing Compile-time Dependencies桺art 3
Difficulty: 7
Now the unnecessary headers have been removed, and needless dependencies on the internals of the class have been eliminated. Is there any further decoupling that can be done? The answer takes us back to basic principles of solid class design.
The Incredible Shrinking Header has now been greatly trimmed, but there may still be ways to reduce the dependencies further. What further #includes could be removed if we made further changes to X, and how?
This time, you may make any changes to X as long as they don't change its public interface so that existing code that uses X is unaffected beyond requiring a simple recompilation. Again, note that the comments are important.
// x.h: after converting to use a Pimpl // to hide implementation details//#include <iosfwd>#include "a.h" // class A (has virtual functions)#include "b.h" // class B (has no virtual functions)class C;class E;class X : public A, private B{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_; // opaque pointer to forward-declared class};inline std::ostream& operator<<( std::ostream& os, const X& x ){ return x.print(os);}
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.
[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
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 Aclass 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!
- Item 28. Minimizing Compile-time Dependencies part 3
- Item 27. Minimizing Compile-time Dependencies part 2
- Item 26. Minimizing Compile-time Dependencies part 1
- Item 26. Minimizing Compile-time Dependencies part 1
- Minimizing Compile-time Dependencies 1
- Part 50 - Detect errors in views at compile time
- [翻译] Effective C++, 3rd Edition, Item 41: 理解 implicit interfaces(隐式接口)和 compile-time polymorphism(编译期多态)
- NdComPlatform_SNS COMPILE TIME
- Compile-time Functions
- Compile time& link time&run time
- 基础一:Android Studio 的 dependencies compile
- late-binding, Compile time, run-time, polymorphism
- Java compile-time vs. run-time
- operator sizeof ---compile time calculation
- Memory Ordering at Compile Time
- 【转载】Runtime vs Compile time
- Runtime vs Compile-Time Classpath
- Memory Ordering at Compile Time
- Item 30. The "Fast Pimpl" Idiom
- IBM-用例建模指南
- 熊猫烧香 - 核心源码 (僅供研究使用!後果自行負責 )
- Item 29. Compilation Firewalls
- 实例解析继承体系重构及ORM映射
- Item 28. Minimizing Compile-time Dependencies part 3
- 某外企业的程序员素质定位题,看看你自己的如何定位的呢?
- Item 27. Minimizing Compile-time Dependencies part 2
- Item 26. Minimizing Compile-time Dependencies part 1
- TOMCAT首页设置
- sizeof()使用的几点注意 以及小数转二进制数
- Item 26. Minimizing Compile-time Dependencies part 1
- 三十而立!
- 通过web.config设置数据库连接串