PIMPL Pattern Guide¶
This document introduces the PIMPL (Pointer to Implementation) pattern used in the Qwt project, helping developers correctly use the project-provided macro definitions.
Key Features¶
Features
- ✅ Hide Implementation Details: Keep private members and data hidden in the implementation class
- ✅ Reduce Header Dependencies: Lower compilation dependencies and speed up build times
- ✅ Binary Compatibility: Changes to implementation do not affect binary compatibility
- ✅ Unified Macros: The project provides standard macros to simplify implementation
What is the PIMPL Pattern¶
PIMPL (Pointer to Implementation) is a C++ design pattern, also known as the "Compilation Firewall" pattern.
Drawbacks of the Traditional Approach¶
1 2 3 4 5 6 7 8 9 | |
Advantages of the PIMPL Pattern¶
1 2 3 4 5 6 7 8 9 10 | |
PIMPL Pattern Advantages
- Build Speed: Modifying private members only requires recompiling a single cpp file
- Binary Compatibility: Changes to private members do not affect ABI compatibility
- Clean Headers: Header files only contain public interface declarations
Qwt PIMPL Macros¶
The project defines the following macros in src/qwt_global.h:
| Macro | Purpose | Usage Location |
|---|---|---|
QWT_DECLARE_PRIVATE(Class) |
Declare private data pointer | In class declaration |
QWT_DECLARE_PUBLIC(Class) |
Declare public host pointer | In PrivateData class |
QWT_PIMPL_CONSTRUCT |
Initialize private data pointer | Constructor initializer list |
QWT_D(name) |
Get private data pointer (non-const) | In member functions |
QWT_DC(name) |
Get private data pointer (const) | In const member functions |
Implementation Steps¶
Step 1: Declare Private Pointer in Header File¶
Use the QWT_DECLARE_PRIVATE macro in the class declaration:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | |
Step 2: Define the PrivateData Class in the cpp File¶
Define the private data class in the cpp file, using the QWT_DECLARE_PUBLIC macro:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | |
Step 3: Constructor Initialization¶
Use the QWT_PIMPL_CONSTRUCT macro to initialize the private pointer:
1 2 3 4 5 6 7 8 9 | |
Macro Expansion
QWT_PIMPL_CONSTRUCT expands to: m_data(qwt_make_unique<PrivateData>(this))
Step 4: Destructor Handling¶
The destructor automatically deletes private data:
1 2 3 4 | |
Step 5: Accessing Private Data in Member Functions¶
Use the QWT_D and QWT_DC macros to access private data:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | |
Complete Example¶
classDiagram
class MyClass {
+QWT_DECLARE_PRIVATE(MyClass)
+setTitle(title : QString)
+title() : QString
-m_data : unique_ptr~PrivateData~
}
class PrivateData {
+QWT_DECLARE_PUBLIC(MyClass)
+title : QString
+value : int
+data : QVector~double~
-q_ptr : MyClass*
}
MyClass --> PrivateData : m_data
PrivateData --> MyClass : q_ptr
Header File MyClass.h¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | |
Source File MyClass.cpp¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | |
Best Practices¶
When to Use the PIMPL Pattern¶
Recommended Scenarios
- The class has many private members or private methods
- The class header has many dependencies and you want to reduce compilation dependencies
- Binary compatibility needs to be maintained (e.g., public API of a library)
- Private members may change frequently
When PIMPL is Not Necessary¶
Optional Scenarios
- Simple, small classes with few private members
- Internal helper classes that do not need to hide their implementation
- Performance-sensitive scenarios (PIMPL has a slight overhead from indirect access)
Important Notes¶
Important Reminders
- Constructor Must Initialize: Use
QWT_PIMPL_CONSTRUCTor manually initializem_data - Use QWT_DC for Const Functions: Ensure const correctness
- PrivateData Constructor Parameter: Must accept the host pointer to initialize
q_ptr - Do Not Expose PrivateData in the Header: The PrivateData class definition should only exist in the cpp file
Macro Expansion Details¶
Below are the macro expansion results to help understand how they work:
| Macro | Expansion |
|---|---|
QWT_DECLARE_PRIVATE(Class) |
std::unique_ptr<PrivateData> m_data; + d_func() method |
QWT_DECLARE_PUBLIC(Class) |
Class* q_ptr; + q_func() method |
QWT_PIMPL_CONSTRUCT |
m_data(qwt_make_unique<PrivateData>(this)) |
QWT_D(name) |
Class::PrivateData* name = d_func(); |
QWT_DC(name) |
const Class::PrivateData* name = d_func(); |