Old words rejidified: behind the PIMPL idiom method
Liu Weipeng
The PIMPL usual technique is too old, and the old people have already remembered when it was proposed. Dongdong, which is like this, is almost certainly nothing to say.
This article is no exception, but we don't want to make any new idea, but a mechanism behind the PIMPL as an exploration and summary.
City door fires and pool fish
The way of use of PIMPL usual techniques is very clear, and its main role is to unlock the use interface and implementation of the interface. If you don't use the PIMPL idiom, the code will be like this:
//c.hpp
#include
Class C
{
PUBLIC:
Void F1 ();
Private:
X x; / / strong coupling with X
}
Like the code above, C and its implementation are strongly coupled, from semantics, X member data is the implementation part of C, should not be exposed to the user. From the essential language, in the user's code, each time use "New C" and "C C
1"
Such a statement will encode X of X to the compiled binary code segment (if the X has a virtual function, it is not these) - because the statement like "New C", is actually equivalent to Operator NEW (SIZEOF (C)) follows up with the constructor of C, "C C
1"
Then, the SIZEOF (C) size is spatially in the current stack, and then the constructor of C is called. Therefore, each X class has been changed, and the source files using C.HPP must be recompiled once, because the size of X may change.
In a large project, this coupling may have a considerable impact on Build time.
PIMPL idiom can eliminate this coupling, using the PIMPL idiom method like this:
//c.hpp
Class X; // Replace Include with a preamble
Class C
{
...
Private:
X * pimpl; // Declare a x *, Class X does not have to be fully defined
}
On a predetermined platform, the size of any pointer is the same. The reason why it is divided into X *, y * These various pointers are mainly to provide a high-level abstract semantic, that is, the pointer pointing to the object of that class, and also gives a compiler, so that it can be correct The operation of the user (such as a member function called X) is resolved and checked. However, if each pointer is only a 32-bit long integer (64 bits), depending on the current hardware on the 64-bit machine.
Because the PIMPL is a pointer, the binary information of X here is not coupled to the use interface of C, that is, when the user "new c" or "c c)
1"
When the compiler generated, no information is doped, and when the user uses C, use C's interface, and is independent of X, so that X is completely isolated from this pointer. . Only C knows and can operate the X object to which the PIMPL member points to.
Firewall
"Modifying the definition of X will cause all source files that use C to recompile" this kind of thing is better than "City gate fire, 殃 and pool fish", the reason is that "the moat" is too close to "city gate" is too close (coupled).
PIMPL usual techniques have become "compiled firewall", what is "firewall", pointer? Not. C compilation mode is "separated compilation", that is, different source files are compiled separately. That is, 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 head file, if the header file "Fire" is? The header file cannot be directly compiled, which is included in the source file and compiled together as part of the source file.
That is to say, if the source file S.cpp uses C.HPP, then Class C (interface part) variation will not be avoided restructing S.CPP. However, the Class X as the implementation of Class C is completely not resco-compilation of S.CPP.
Therefore, we need to isolate Class X outside C.HPP. Thus, each source file using Class C is separated from Class X (not in the same compilation unit with CLASS X). However, since Class C uses Class X objects to be used as its implementation, it is inevitable to "dependence" on Class X. However, this "dependency" should be described as: "The implementation of Class C depends on Class X", and should not be "Class C users use the interface part dependent on Class X".
If we directly write X of the object in the data member of Class C, it will be apparent. Use the Class C "" See "What doesn't" see "- Class X-Class X-Class X-Class X-Class X-Class X-Class X-Class X-Class X-Class X-Class X-Class X - there is a coupling between them. However, if you use a pointer to the Class X, you can "push" to the Class C implementation file, where we #include "x.hpp", define all member functions, and depend The implementation of X, it doesn't matter, because C is implemented in X, it is important: At this time, Class X will only cause the Class C to re-compile, and the source file using Class C is safe and sound. !
The pointer acts as a bridge here. Relying on the information "push" to another compiler, isolated from the user. The firewall is inherent attributes of the C compiler.
Crossing C compile firewall
What is crossing the C compile fire wall? Is a pointer! What is the object refers to the source file of the pointer, but does not have to "see" that object directly - it may be in another compiler, which is the pointer crossing the compile period firewall, which is connected to that object.
In a sense, it is impossible to cross the C compile firewall as long as the symbol representing the address, and the symbol of the constructs cannot be.
For example, a function name, it refers to the start address of the function code, so the function can declare in a compiler, but define in another compiler, the compiler will be connected to them. Users can use it as long as the declaration of the function can be used. Unlike the class, the class name is a language structure, using the class, must know the definition of the class, otherwise the binary code cannot be generated. The symbol of the variable is essentially the address, but the use of variables generally requires the definition of variables, and the extern modifier can be placed in another compilation unit.