Item 27. Minimizing Compile-time Dependencies part 2
2007-03-01 11:03
141 查看
I l@ve RuBoard |
Item 27. Minimizing Compile-time Dependencies桺art 2Difficulty: 6Now that the unnecessary headers have been removed, it's time for Phase 2: How can you limit dependencies on the internals of a class? Below is how the header from Item 26 looks after the initial clean-up pass. What further #includes could be removed if we made some suitable changes, and how? This time, you may make changes to X as long as X's base classes and its public interface remain unchanged; any current code that already uses X should not be affected beyond requiring a simple recompilation. // x.h: sans gratuitous headers // #include <iosfwd> #include <list> // None of A, B, C or D are templates. // Only A and C have virtual functions. #include "a.h" // class A #include "b.h" // class B #include "c.h" // class C #include "d.h" // class D 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: std::list<C> clist_; D d_; }; inline std::ostream& operator<<( std::ostream& os, const X& x ) { return x.print(os); } |
I l@ve RuBoard |
I l@ve RuBoard |
SolutionThere are a couple of things we weren't able to do in the previous problem.We had to leave a.h and b.h. We couldn't get rid of these because X inherits from both A and B, and you always have to have full definitions for base classes so that the compiler can determine X's object size, virtual functions, and other fundamentals. (Can you anticipate how to remove one of these? Think about it: Which one can you remove, and why/how? The answer will come shortly.) We had to leave list, c.h, and d.h. We couldn't get rid of these right away because a list<C> and a D appear as private data members of X. Although C appears as neither a base class nor a member, it is being used to instantiate the list member, and most current compilers require that when you instantiate list<C>, you are able to see the definition of C. (The standard doesn't require a definition here, though, so even if the compiler you are currently using has this restriction, you can expect the restriction to go away over time.) Now let's talk about the beauty of Pimpls. (Yes, really.) C++ lets us easily encapsulate the private parts of a class from unauthorized access. Unfortunately, because of the header file approach inherited from C, it can take a little more work to encapsulate dependencies on a class's privates. "But," you say, "the whole point of encapsulation is that the client code shouldn't have to know or care about a class's private implementation details, right?" Right, and in C++, the client code doesn't need to know or care about access to a class's privates (because unless it's a friend, it isn't allowed any), but because the privates are visible in the header, the client code does have to depend upon any types they mention. How can we better insulate clients from a class's private implementation details? One good way is to use a special form of the handle/body idiom (Coplien92) (what I call the Pimpl Idiom because of the intentionally pronounceable pimpl_ pointer[1]) as a compilation firewall (Lakos96, Meyers98, Meyers99, Murray93). [1] I always used to write impl_. The eponymous pimpl_ was actually coined several years ago by Jeff Sumner (chief programmer at PeerDirect), due in equal parts to a penchant for Hungarian-style "p" prefixes for pointer variables and an occasional taste for horrid puns. A Pimpl is just an opaque pointer (a pointer to a forward-declared, but undefined, helper class) used to hide the private members of a class. That is, instead of writing: // file x.h class X { // public and protected members private: // private members; whenever these change, // all client code must be recompiled }; we write: // file x.h class X { // public and protected members private: struct XImpl; XImpl* pimpl_; // a pointer to a forward-declared class }; // file x.cpp struct X::XImpl { // private members; fully hidden, can be // changed at will without recompiling clients }; Every X object dynamically allocates its XImpl object. If you think of an object as a physical block, we've essentially lopped off a large chunk of the block and in its place left only "a little bump on the side"梩he opaque pointer, or Pimpl. The major advantages of this idiom come from the fact that it breaks compile-time dependencies. Types mentioned only in a class's implementation need no longer be defined for client code, which can eliminate extra #includes and improve compile speeds. A class's implementation can be changed梩hat is, private members can be freely added or removed, without recompiling client code. The major costs of this idiom are in performance. Each construction/destru 4000 ction must allocate/deallocate memory. Each access of a hidden member can require at least one extra indirection. (If the hidden member being accessed itself uses a back pointer to call a function in the visible class, there will be multiple indirections.) We'll come back to these and other Pimpl issues later in this section. For now, in our example, there were three headers whose definitions were needed simply because they appeared as private members of X. If we instead restructure X to use a Pimpl, we can immediately make several further simplifications. Use the Pimpl idiom to hide the private implementation details of X. #include <list> #include "c.h" // class C #include "d.h" // class D One of these headers (c.h) can be replaced with a forward declaration, because C is still being mentioned elsewhere as a parameter or return type, and the other two (list and d.h) can disappear completely. Guideline
// x.h: after converting to use a Pimpl // #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); } The private details go into X's implementation file, where client code never sees them and therefore never depends upon them. // Implementation file x.cpp // struct X::XImpl { std::list<C> clist_; D d_; }; That brings us down to including only three headers, which is a great improvement. But it turns out that there is still a little more we could do, if only we were allowed to change the structure of X more extensively. This leads us nicely into Item 28… |
I l@ve RuBoard |
相关文章推荐
- Item 28. Minimizing Compile-time Dependencies part 3
- Item 26. Minimizing Compile-time Dependencies part 1
- Item 26. Minimizing Compile-time Dependencies part 1
- Part 7 - Setting an item selected when an asp.net mvc dropdownlist is loaded
- 求职类App原型制作分享-Part-time Clouds
- 论文阅读笔记: 2017 cvpr Realtime Multi-Person 2D Pose Estimation using Part Affinity Fields
- Effective C++学习笔记(Part One:Item 1-4)
- Effective C++学习笔记(Part Five:Item 26-31)
- Part Itemtype解读(4): Event的响应机制设定
- Item 35. Memory Management part 1
- Item 31. Name Lookup and the Interface Principle part 1
- Error: Found item Attr/font more than one time Error: Execution failed for task
- Undefined symbols for architecture i386: "_AVPlayerItemDidPlayToEndTimeNotification", referenced f
- 8.python之面相对象part.7(__setitem__,__getitem,__delitem__)
- SmartClient Software factory中的Composite UI Application Block(Cab)技术了解(二):WorkItem&SmartPart
- Effective C++学习笔记(Part Five:Item 26-31)
- [翻译] Effective C++, 3rd Edition, Item 41: 理解 implicit interfaces(隐式接口)和 compile-time polymorphism(编译期多态)
- Effective C++学习笔记(Part One:Item 1-4)
- Sitecore Digital Marketing System, Part 1: Creating personalized, custom content for site visitors(自定义SiteCore中的 Item的Personalize的Condition) -摘自网络
- Advanced Run Time Type Identification in C++ Part II(Property Library An Implementation of RTTI in C++)