PIMPL 模式使用指南¶
Qwt 用 PIMPL 模式隐藏私有成员,相关宏的用法如下。
主要功能特性¶
该模式把私有成员放在头文件之外,减少编译依赖,不影响二进制兼容性,并依赖一组固定的项目宏。
什么是 PIMPL 模式¶
PIMPL(Pointer to Implementation,指向实现的指针)是一种 C++ 设计模式,也称为"编译防火墙"模式。
传统方式的缺陷¶
不使用 PIMPL 时,所有私有成员都直接暴露在头文件中,传统写法如下:
1 2 3 4 5 6 7 8 9 | |
PIMPL 模式的优势¶
改用 PIMPL 后,头文件只保留公共接口和一行 QWT_DECLARE_PRIVATE 声明,全部私有成员隐藏进 MyClass::PrivateData:
1 2 3 4 5 6 7 8 9 10 | |
提示 — PIMPL 模式优势
- 编译速度:修改私有成员只需重新编译单个 cpp 文件
- 二进制兼容:私有成员变化不影响 ABI 兼容性
- 头文件整洁:头文件只包含公共接口声明
Qwt PIMPL 宏定义¶
项目在 src/qwt_global.h 中定义了以下宏:
| 宏名称 | 用途 | 使用位置 |
|---|---|---|
QWT_DECLARE_PRIVATE(Class) |
声明私有数据指针 | 类声明中 |
QWT_DECLARE_PUBLIC(Class) |
声明公有宿主指针 | PrivateData 类中 |
QWT_PIMPL_CONSTRUCT |
初始化私有数据指针 | 构造函数初始化列表 |
QWT_D(name) |
获取私有数据指针(非常量) | 成员函数中 |
QWT_DC(name) |
获取私有数据指针(常量) | const 成员函数中 |
实现步骤详解¶
步骤 1:头文件中声明私有指针¶
在类声明中使用 QWT_DECLARE_PRIVATE 宏:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | |
步骤 2:cpp 文件中定义 PrivateData 类¶
在 cpp 文件中定义私有数据类,使用 QWT_DECLARE_PUBLIC 宏:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | |
步骤 3:构造函数初始化¶
使用 QWT_PIMPL_CONSTRUCT 宏初始化私有指针:
1 2 3 4 5 6 7 8 9 | |
信息 — 宏展开说明
QWT_PIMPL_CONSTRUCT会展开为:m_data(qwt_make_unique<PrivateData>(this))
步骤 4:析构函数处理¶
析构函数中自动删除私有数据:
1 2 3 4 | |
步骤 5:成员函数访问私有数据¶
使用 QWT_D 和 QWT_DC 宏访问私有数据:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | |
完整示例¶
下面的类图展示了 MyClass 与 PrivateData 通过 m_data 和 q_ptr 互相引用的结构:
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
头文件 MyClass.h¶
完整的头文件只声明公共接口和 QWT_DECLARE_PRIVATE(MyClass) 宏,不暴露任何私有成员:
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 | |
源文件 MyClass.cpp¶
源文件用 QWT_DECLARE_PUBLIC 定义 MyClass::PrivateData,通过 QWT_PIMPL_CONSTRUCT 初始化,并用 QWT_D/QWT_DC 访问私有数据:
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 | |
最佳实践¶
何时使用 PIMPL 模式¶
提示 — 建议使用场景
- 类有大量私有成员或私有方法
- 类的头文件依赖较多,希望减少编译依赖
- 需要保持二进制兼容性(如库的公共 API)
- 私有成员可能频繁变化
何时不必使用 PIMPL¶
信息 — 可选场景
- 简单的小型类,私有成员很少
- 内部使用的辅助类,不需要隐藏实现
- 性能敏感的场景(PIMPL 有轻微的间接访问开销)
注意事项¶
警告 — 重要提醒
- 构造函数必须初始化:使用
QWT_PIMPL_CONSTRUCT或手动初始化m_data- const 函数使用 QWT_DC:确保 const 正确性
- PrivateData 构造函数参数:必须接受宿主指针用于初始化
q_ptr- 不要在头文件暴露 PrivateData:PrivateData 类定义只在 cpp 文件中
宏展开原理¶
各宏的展开结果如下:
| 宏 | 展开结果 |
|---|---|
QWT_DECLARE_PRIVATE(Class) |
std::unique_ptr<PrivateData> m_data; + d_func() 方法 |
QWT_DECLARE_PUBLIC(Class) |
Class* q_ptr; + q_func() 方法 |
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(); |