一、什么是PImpl模式?
简单说:把类的秘密藏在指针后面,让使用者看不到内部实现。
就像你去餐厅吃饭:
头文件(.h)= 菜单(只告诉你有什么菜)
实现文件(.cpp)= 后厨(具体怎么做你看不到)
PImpl指针= 传菜窗口(你通过它拿到菜,但进不去后厨)
二、为什么要用PImpl?
问题1:编译慢
cpp
// 没有PImpl - 你的头文件长这样 // MyClass.h #include <tensorflow/core/public/session.h> // 巨长的头文件 #include <opencv2/opencv.hpp> // 也很长 #include <vector> #include <string> class MyClass { tensorflow::Session* session; // 暴露了TensorFlow cv::Mat image; // 暴露了OpenCV std::vector<float> data; };后果:
谁
#include "MyClass.h"都要编译TensorFlow和OpenCV(几百万行代码)改一行实现,所有依赖的文件都要重新编译(可能几十分钟)
cpp
// 有PImpl - 头文件非常干净 // MyClass.h #include <memory> // 只需要智能指针 class MyClass { class Impl; // 声明:我有秘密 std::unique_ptr<Impl> pImpl; // 藏起来的指针 public: void doSomething(); };好处:
头文件只有几行,编译飞快
改实现只编译一个.cpp文件(几秒钟)
问题2:隐藏依赖
cpp
// 你的用户只需要知道这些 MyClass obj; obj.doSomething(); // 简单! // 不需要知道: // - TensorFlow怎么用 // - OpenCV怎么处理图像 // - 内存怎么管理
问题3:ABI兼容性
cpp
// 没有PImpl:改了成员变量 class MyClass { int a; // 老版本 // int b; // 新版本加了这个 → 内存布局变了! }; // 有PImpl:随便改实现 class MyClass::Impl { int a; // 老版本 int b; // 新版本加这个 → 用户代码不受影响! };三、PImpl怎么用?
传统写法(没有PImpl)
cpp
// Calculator.h class Calculator { int secret_data; std::string complex_logic; public: int add(int a, int b) { return a + b; } };PImpl写法
cpp
// Calculator.h - 公开头文件(给用户看) class Calculator { class Impl; // 声明:我有内部实现 std::unique_ptr<Impl> pImpl; // 指向实现的指针 public: Calculator(); // 构造函数 ~Calculator(); // 析构函数 int add(int a, int b); // 公开接口 }; // Calculator.cpp - 实现文件(藏着) class Calculator::Impl { public: int secret_data; std::string complex_logic; int add(int a, int b) { return a + b; // 真正的实现在这里 } }; Calculator::Calculator() : pImpl(std::make_unique<Impl>()) {} Calculator::~Calculator() = default; int Calculator::add(int a, int b) { return pImpl->add(a, b); // 转发给Impl }