Pimpl Idiom/handle Body Idiom

Source: Internet
Author: User
Tags c constructor

When reading "Effective C + +" and project source code, see Pimpl Idiom. It can be used toreduce compilation dependencies between files, by dividing a class into two classes, one providing only the interface, and the other responsible for implementing the interface to separate the interface from the implementation. The key to this separation is to replace the "defined dependency" with a declared dependency, and the essence of the minimization of the compilation dependency is to make the header file as self-fulfilling as possible, and to have it dependent on the declarative (rather than the defined) in other files.


Refer to some of the descriptions here:
The Pimpl idiom, also known as the compilation firewall or Cheshire Cat technique, is a "private implementation" technique Useful Ceeplusplus and statically compiled languages like it ...

Benefits:

    1. Changing private member variables of a class does not require recompiling classes this depend on it, thus make Ti Mes is faster, and Thefragilebinaryinterfaceproblem is reduced.
    2. the header file does not need to #include classes that is used ' by value ' in private member variables, thus Compi Le times is faster.
    3. This was sorta like the SmallTalk automatically handles classes ... more pure encapsulation.

Drawbacks:

    1. More work for the implementor.
    2. Doesn ' t work for ' protected ' members where access was subclasses is required.
    3. Somewhat harder to read code, since some information was no longer in the header file.
    4. Run-time performance is slightly compromised due to the pointer indirection, especially if function calls is virtual (bra NCH prediction for indirect branches is generally poor).

How to do it:

    1. put all the private member variables into a struct.
    2. put the struct definition in the. cpp file.
    3. in the header file, put only the  forwarddeclaration of the struct.
    4. in the class definition, declare a (SMART) pointer to the struct as the only Priva TE member variable.
    5. the constructors for the class need to create the struct.
    6. the destructor of the class needs to destroy the struct (possibly implicitly due t o Use of a smart pointer).
    7. the assignment operator and copyconstructor need to copy the struct Appropriately or else be disabled.

Code:

1 struct AIMP;
2 class A {
3 Public:
4//Same public interface as A, but all delegated to concrete implementation.
5 Private:
6 AIMP * PIMPL;
7};
8



If you use a smartpointer and you only has one implementation, there is no need to make any of the member functions Virtu Al, except possibly the destructor. The run-time cost of non-virtual member function calls are much lower, and a compiler that does Whole-program optimization Can inline them even though they ' re in a separate translation unit. Here's an example:

1//Foo.h
2
3 class Foo_impl;
4
5 class Foo {
6//Boilerplate
7 Friend class Foo_impl;
8 foo () {}/Foo_impl can derive from Foo
9 Const FOO_IMPL * IMPL () const;
Ten Foo_impl * IMPL ();
Public:
Virtual ~foo () {}
//Factories
The static std::auto_ptr<foo> create (int value);
//Interface
int value () const;
17};
18
//Foo.cpp
20
Class Foo_impl:public Foo {
Friend class Foo;
//Constructors mirroring the factory functions in Foo
explicit Foo_impl (int value): Value_ (value) {}
//Member Data
Value_ int;
27};
28
Inline Const FOO_IMPL * FOO::IMPL () const {
return static_cast<const Foo_impl *> (this);
31}
Inline Foo_impl * Foo::impl () {
Return Static_cast<foo_impl *> (this);
34}
35
std::auto_ptr<foo> foo::create (int value) {
PNS return std::auto_ptr<foo> (new Foo_impl (value));
38}
39
Foo::value int () const {return impl ()->value_;}
41
42

Here, the destructor needs is declared virtual Foo so, std::auto_ptr<foo> calls Foo_impl ' s destructor. If you use boost::shared_ptr<foo> instead, even this doesn ' t need to is virtual, because shared_ptr remembers how to Call the correct destructor. (This doesn ' t improve performance or memory use, because shared_ptr are larger and slower than auto_ptr, but if you need to Use shared_ptr anyway-as well eliminate the virtual destructor.) --Benhutchings


Reference reading:

Effective C + +
Http://c2.com/cgi/wiki?PimplIdiom
Http://en.wikipedia.org/wiki/Opaque_pointer

-----------------------------

Pimpl mechanism for the design of [C + +] programs first, the problems encountered

1. Hide the implementation

When we provide the interface to the client, we only want to expose its interface and hide its implementation or algorithm. At this time, at least two options are available:

(1) write an abstract class and then inherit it

(2) Use Pimpl to put the implementation into the internal public file, and hide it externally.

2. Recompile

When we have a very large project, our bottom-level header file does not want to be modified, because this causes all the source files containing the header to be recompiled.

Ii. What is the PIMPLL mechanism

1.Private implementation

The direct literal meaning is "privatization", and as we often hear suggestions such as "Do not change your public interface", the Pimpl mechanism, as the name implies, will be privatized, trying to make the header file opaque to change. The main role is to unlock the use of the class interface and implementation of the coupling.

2.pointer to implementation

This statement is semantically more concerned with the way the code is implemented, which is a pointer to the implementation.

3. Bridging mode

In fact, this is also a simple bridging mode

third, the specific analysis

1. Non-use of Pimpl

  
  
  
  
  5:   
  
  7:  
  
  
13:   
14:};

As you can see, if a new member function is added to the base, or if it is changed, the sub-class sub of the file is recompiled. In a large project, such a modification could lead to a spike in recompilation time.

2. A slightly better approach

In general, it is a good practice not to include header files in a header file, but this does not completely eliminate the recompilation cost of modifying base.h. A slightly better approach is to include base.h only in Sub.cpp, but this is still a recompile, just a bit more perfect in performance.

3. Conditions of use of the mechanism

We use the predecessor to declare a Impl class and place a pointer instance of the class into the main class. After that we only modify the contents of the Impl class inside private.

  
  
  
  
  
  6:   
  
  8:   
  9:};

Unless we modify the public interface of base, the header file will not be modified. We then use the implementation of this IMPL class to accomplish the detail implementation of the main class, and in the constructor of the main class, we complete the instantiation of the implementation class pointer:

  
  
  3:  
  
  
  
  
  8:  
  
12:   
16:   
17:   
18:     
19:   
20:   
:   {     
22:   
24:  
08x   
29:}

4. Practice

In practice, internal classes are often used to complete the PIMPL mechanism.

An example on the Internet

  
  
  
  4:public:  
  
  6:   
  7:   
  8:}  
  9:  
12: {  
13:  
14:};  
15:  
18:   
19:}
Iv. Other problems that are attracted

1. Efficiency issues, the increase in each class, will certainly increase the cost.

2. This mechanism is not necessarily the best mechanism, the simplest mechanism is the best mechanism

3. In the construction and destruction of the time, because we want to new and to ensure that the delete, will lead to the Raii principle of the resource management class copy behavior problem

So, a better approach is that we can use a more secure smart pointer when using this mechanism, such as scoped_ptr and shared_ptr, but shared_ptr is usually more appropriate because it supports copying and assigning values.

  1:class sample   
  2: {  
  3:private:     
  4:   class Impl;  Incomplete internal class declaration     
  5:   shared_ptr<impl> p;//SHARED_PTR member variable  
  6:public:      
  7:   sample ();   constructor function    
  8:   void print ();  Interfaces available to the outside world   
  9:};    
10:  
11://Complete definition of the Impl class and other features in the CPP for sample:    
12:class Sample::impl//internal class implementation  
13: {  
14:public:      
:   void Print ()       
:   {    
17:     
:   }   
19:};    
20:  
21://constructor initialization shared_ptr  void Sample::p rint ()  
22:sample::sample ():p (new Impl) {}            
23: {   
:   //Call Pimpl to implement print ()   
25:   

Can be used directly:

  1:sample s;  
  

(in more effective C + + Item M14 There is a discussion of this issue, and then we do the study discussion)

v. References

In the course of the article Learning Summary, reference and deduce some of the content of the link, here to express our thanks.

You can reprint this article at will, but want to guarantee the completeness, give the following reference link

This article links:

http://pppboy.blog.163.com/blog/static/3020379620115185274478/

Reference connection:

Http://book.51cto.com/art/201009/225853.htm

Http://blog.csdn.net/pongba/archive/2004/09/12/102232.aspx

Http://blog.csdn.net/llu131313/archive/2011/06/01/6460423.aspx

Http://blog.sina.com.cn/s/blog_48f93b530100n2bc.html

Pimpl (Private Implementation)
Sputter implicating
The use of the Pimpl idiom is well known, and its main function is to solve the coupling of the interface and implementation of the class. If you do not use Pimpl idioms, the code will look like this:
C.hpp
#include <x.hpp>
Class C
{
Public
void F1 ();
Private
x x; Strong coupling to X
};
Like the code above, C is strongly coupled with its implementation, semanticallyThat the x member data is part of the implementation of C and should not be exposed to the user. fromIn essence, in the user's code, each time a statement such as "New C" and "C C1" is used, the size of x is hard-coded into the compiled binary snippet (if x has a virtual function)-this is because, for a statement such as "New C", In fact, the equivalent of operator new (sizeof (c)) followed by the C constructor, and "C C1" is on the current stack to free the size of sizeof (c) space, and then call C's constructor. Therefore, every time the X class is changed, the source files that use c.hpp must be recompiled once, because the size of x may change.
In a large project, this coupling can have a significant impact on build time.
Pimpl idioms can be used to eliminate this coupling, using the Pimpl idiom code like this:
C.hpp
Class X; Replace include with a leading declaration
Class C
{
...
Private
x* Pimpl; When declaring a x*, class X does not have to be fully defined
};
On a given platform, the size of any pointer is the same. These various pointers are divided into x*,y*, which mainly provide a high-level abstract semantics, that is, the pointer to the object of that class, and also to the compiler a directive, so that the user can do the correct operation (such as invoking the member function of x) and check the resolution. However, from a run-time perspective, each pointer is just a 32-bit long integer (64 bits on a 64-bit machine, depending on the current hardware).
Because Pimpl is a pointer, the binary information here (sizeof (C) and so on) is not coupled to the use of C interface, that is, when the user "new C" or "C C1", the compiler generated code does not doping any information of x, and when the user uses C, Using the C interface is also independent of x, so x is completely isolated from the user by this pointer. Only C knows and can manipulate the X object that the Pimpl member points to.

Firewall
"Modifying the definition of x causes all source files that use C to be recompiled" is like "sputter, implicating" because the moat is too close to the "gate" (coupling).
Pimpl idiom is also a "compile-time Firewall", what is the "firewall", pointers? No. c+the compilation mode of + is "split compilation", that is, different source files are compiled separately. In other words, there is a natural firewall between different source files, and a source file "fire" does not affect another source file。
However, here we consider the header file, if the header file "fire" and how?The header file cannot be compiled directly, it is contained in the source file, and is compiled together as part of the source file.
This means that if the source file S.cpp uses C.HPP, then the change of Class C (the interface part) will inevitably lead to S.CPP recompilation. However, Class X, which is the implementation part of Class C, should not cause S.cpp to be recompiled at all.
Therefore, we need to isolate class X from C.HPP. Thus, each source file that uses Class C is separated from class X (not the same compilation unit as Class X). However, since Class C uses the object of Class X as its implementation part, it is unavoidable to "rely" on class X. However, this "dependency" should be described as: "The implementation of Class C is dependent on Class X", and should not be "Class C users using the interface part depends on Class X".
If we write the object of X directly inside the data member of Class C, it is obvious that the user using Class C "sees" something that should not be "seen"--class x--the coupling between them. However, if you use a referenceWith a pointer to class X, you can "push" the binary information of x into the implementation file of Class C, whereWe include "X.HPP", define all of the member functions, and rely on the implementation of X, this does not matter, because the implementation of C is inherently dependent on X, it is important that the change of Class X will only cause the implementation of Class C file recompile, and the user using class C's source file is safe!
The pointer acts as a bridge here. "Pushes" dependency information into another compilation unit, isolating it from the user. The firewall is the intrinsic property of the C + + compiler.

Through the C + + compile-time firewall
What is it that crosses the C + + compile-time firewall? It's a pointer! The source file that uses the pointer "knows" what object the pointer refers to, but does not have to "see" that object directly-it may be in another compilation unit, the pointer crosses the compile-time firewall and connects to that object.
In a sense, as long as the symbol representing the address can traverse the C + + compile-time firewall, and the symbol representing the structure (constructs) is not.
For example, the function name, which refers to the starting address of the function code, so the function can be declared in a compilation unit, but defined in another compilation unit, the compiler will be responsible for connecting them together. The user can use it as long as it is declared by the function. The class is different, the class name represents a language structure, and the class must know the definition of the class, otherwise it cannot generate binary code. The symbol of a variable is essentially an address, but using a variable generally requires the definition of a variable, while using the extern modifier you can place the definition of the variable in another compilation unit.


This article from Csdn Blog, reproduced please indicate the source: http://www.cnblogs.com/yaukey/archive/0001/01/01/1530640.html

Pimpl Idiom/handle Body Idiom

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.