C++中的Pimpl idiom是什么,有什么好处?(隐藏实现)
技术百科
裘德小鎮的故事
发布时间:2026-01-02
浏览: 次 Pimpl idiom通过将私有成员移至独立实现类并仅在头文件中保留指向它的指针,实现接口与实现分离,核心目的是隐藏实现细节、减少编译依赖。
C++中的Pimpl idiom(Pointer to Implementation,即“指向实现的指针”)是一种通过将类的私有成员数据和实现细节移到一个独立的、不对外暴露的类中,并在主类中仅保留一个指向该实现类的指针,从而实现接口与实现分离的技术。它最核心的目的就是隐藏实现细节,减少编译依赖。
为什么需要隐藏实现?
在传统C++类设计中,头文件通常包含所有私有成员变量的定义(比如std::string、std::vector、自定义类等)。一旦这些类型或其定义发生变化,所有包含该头文件的源文件都必须重新编译——即使它们只调用公有接口。这会显著拖慢大型项目的构建速度,也使库的二进制兼容性更难维护。
Pimpl把所有私有数据打包进一个只有实现文件(.cpp)才看到的结构体或类里,头文件里只留一个std::unique_ptr
基本写法示例
假设有一个Widget类:
立即学习“C++免费学习笔记(深入)”;
- 头文件
widget.h只声明公有接口和一个std::unique_ptrpImpl; -
Impl结构体定义在widget.cpp
中,包含所有原本该放在头文件里的私有成员 - 构造函数、析构函数、拷贝/移动操作需在
.cpp中显式定义(因为Impl类型在头文件中不完整)
主要好处
降低编译耦合:修改Impl内部字段、更换底层容器、升级第三方库类型,都不影响包含widget.h的代码重新编译。
提升二进制兼容性:动态库或SDK发布时,只要公有接口不变,内部重写Impl不会破坏ABI(应用程序二进制接口)。
封装更彻底:用户完全看不到你用了什么算法、缓存策略、辅助对象,连sizeof(Widget)都固定(只取决于指针大小),便于做内存布局控制。
需要注意的代价
每次访问私有数据都要经过一次指针解引用,有轻微运行时开销;额外堆内存分配(可用内存池优化);不能默认生成特殊成员函数,需手动定义(尤其是析构函数要非内联,否则Impl类型不完整会报错);调试时需多跳一层查看pImpl内容。
# 是一种
# 放在
# 到你
# 用了
# 尤其是
# 并在
# 都要
# 都不
# 对象
# 堆
# c++
# String
# 指针
# 构造函数
# 接口
# 为什么
# pointer
# 封装
# 成员变量
# 成员函数
# 析构函数
# 结构体
# 算法
# 头文件
# 类中
相关栏目:
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
AI推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
SEO优化<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
技术百科<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
谷歌推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
百度推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
网络营销<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
案例网站<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
精选文章<?muma echo $count; ?>
】
相关推荐
- Win10怎么卸载剪映_Win10彻底卸载剪映方法
- PythonPandas数据分析教程_数据清洗与处
- Windows10如何更改鼠标图标_Win10鼠标
- Win11文件扩展名怎么显示 Win11查看文件后
- php增删改查在php8里有什么变化_新特性对cu
- 如何在 Go 中创建包含 map 的 slice(
- Go 中 := 短变量声明的类型推导机制详解
- c++怎么操作redis数据库_c++ hired
- php订单日志怎么按金额排序_php按订单金额排序
- PythonWeb前后端整合项目教程_FastAP
- 微信JSAPI支付回调PHP怎么接收_处理JSAP
- 如何在 Go 中正确测试带 Cookie 的 HT
- PHP 中如何在函数内持久化修改引用变量的指向
- c++如何用AFL++进行模糊测试 c++ Fuz
- Win11怎样安装网易云音乐_Win11安装网易云
- Win11怎么用设置清理回收站_Win11设置清理
- Win11怎么关闭通知消息_屏蔽Windows 1
- Go 语言标准库为何不提供泛型切片的 Contai
- Windows10无法连接到Internet_Wi
- Windows10无法识别USB设备描述符请求失败
- Python正则表达式实战_模式匹配说明【教程】
- 获取 PHP 文件最后修改时间的正确方法
- PHP主流架构怎么集成Redis缓存_配置步骤【方
- 如何在Golang中处理模块冲突_解决依赖版本不兼
- Win11怎么关闭系统透明度_Windows11个
- Windows10怎么查看系统激活状态_Windo
- XSLT怎么生成动态的HTML属性名和标签名
- 如何正确访问 Laravel 模型或对象的属性而非
- c# 如何深拷贝和浅拷贝
- Win10怎样设置多显示器_Win10多显示器扩展
- Win11怎么关闭应用权限_Windows11相机
- php查询数据怎么分组_groupby分组查询配合
- 如何在Golang中实现RPC异步返回_Golan
- 如何在 Python 测试中动态配置 @backo
- Win11怎么设置系统还原_Windows11系统
- php485函数执行慢怎么优化_php485性能提
- Win11怎么关闭任务栏小组件_Windows11
- 如何使用Golang捕获测试日志_Golang t
- 如何在 Go 中创建包含映射(map)的切片(sl
- Win11怎么关闭触摸键盘图标_Windows11
- Windows10如何更改任务栏高度_Win10解
- 静态属性修改会影响所有实例吗_php作用域操作符下
- php485返回空数组怎么回事_php485数据接
- Win11怎么查看显卡温度 Win11任务管理器查
- Win10怎样清理C盘爱奇艺缓存_Win10清理爱
- Win11怎么关闭定位服务 Win11禁止应用获取
- Mac如何开启夜览模式_Mac护眼模式设置与定时
- Mac怎么设置登录项_Mac管理开机自启动程序【教
- C++如何使用std::transform批量处理
- 如何使用Golang写入二进制文件_Golang

中,包含所有原本该放在头文件里的私有成员
QQ客服