pImpl的原理

编译防火墙——C++的Pimpl惯用法解析 Pimpl(pointer to implementation, 指向实现的指针)是一种常用的,用来对“类的接口与实现”进行解耦的方法。这个技巧可以避免在头文件中暴露私有细节(见下图1),因此是促进API接口与实现保持完全分离的重要机制。但是Pimpl并不是严格意义上的设计模式(它是受制于C++特定限制的变通方案),这种惯用法可以看作桥接设计模式的一种特例。图1: Pimpl惯用法,这里的公有类拥有 阅读详情
城门失火殃及池鱼
pImpl 惯用手法的运用方式大家都很清楚,其主要作用是解开类的使用接口和实现的耦合。如果不使用 pImpl 惯用手法,代码会像这样:
       //c.hpp
        #include<x.hpp>
class C
        {
        public:
            void f1();
        private:
            X x; // 与 X 的强耦合
        };
像上面这样的代码, C 与它的实现就是强耦合的,从语义上说, x 成员数据是属于 C 的实现部分,不应该暴露给用户。从语言的本质上来说,在用户的代码中,每一次使用 ”new C” 和 ”C c1” 这样的语句,都会将 X 的大小硬编码到编译后的二进制代码段中(如果 X 有虚函数,则还不止这些)——这是因为,对于 ”new C” 这样的语句,其实相当于 operator new(sizeof(C) ) 后面再跟上 C 的构造函数,而 ”C c1” 则是在当前栈上腾出 sizeof(C) 大小的空间,然后调用 C 的构造函数。因此,每次 X 类作了改动,使用 c.hpp 的源文件都必须重新编译一次,因为 X 的大小可能改变了。
在一个大型的项目中,这种耦合可能会对 build 时间产生相当大的影响。
pImpl 惯用手法可以将这种耦合消除,使用 pImpl 惯用手法的代码像这样:
        //c.hpp
        class X; // 用前导声明取代 include
        class C
        {
         ...
         private:
            X* pImpl; // 声明一个 X* 的时候, class X 不用完全定义
        };
在一个既定平台上,任何指针的大小都是相同的。之所以分为 X* , Y* 这些各种各样的指针,主要是提供一个高层的抽象语义,即该指针到底指向的是那个类的对象,并且,也给编译器一个指示,从而能够正确的对用户进行的操作(如调用 X 的成员函数)决议并检查。但是,如果从运行期的角度来说,每种指针都只不过是个 32 位的长整型(如果在 64 位机器上则是 64 位,根据当前硬件而定)。
正由于 pImpl 是个指针,所以这里 X 的二进制信息( sizeof(C) 等)不会被耦合到 C 的使用接口上去,也就是说,当用户 ”new C” 或 ”C c1” 的时候,编译器生成的代码中不会掺杂 X 的任何信息,并且当用户使用 C 的时候,使用的是 C 的接口,也与 X 无关,从而 X 被这个指针彻底的与用户隔绝开来。只有 C 知道并能够操作 pImpl 成员指向的 X 对象。
 
防火墙
“修改 X 的定义会导致所有使用 C 的源文件重新编译”这种事就好比“城门失火,殃及池鱼”,其原因是“护城河”离“城门”太近了(耦合)。
pImpl 惯用手法又被成为“编译期防火墙”,什么是“防火墙”,指针?不是。 C++ 的编译模式为“分离式编译”,即不同的源文件是分开编译的。也就是说,不同的源文件之间有一道天然的防火墙,一个源文件“失火”并不会影响到另一个源文件。
但是,这里我们考虑的是头文件,如果头文件“失火”又当如何呢?头文件是不能直接编译的,它包含于源文件中,并作为源文件的一部分被一起编译。
这也就是说,如果源文件 S.cpp 使用了 C.hpp ,那么 class C 的(接口部分的)变动将无可避免的导致 S.CPP 的重新编译。但是作为 class C 的实现部分的 class X 却完全不应该导致 S.cpp 的重新编译。
因此,我们需要把 class X 隔绝在 C.hpp 之外。这样,每个使用 class C 的源文件都与 class X 隔离开来(与 class X 不在同一个编译单元)。但是,既然 class C 使用了 class X 的对象来作为它的实现部分,就无可避免的要“依赖”于 class X 。只不过,这个“依赖”应该被描述为:“ class C 的实现部分依赖于 class X ”,而不应该是“ class C 的用户使用接口部分依赖于 class X ”。
如果我们直接将 X 的对象写在 class C 的数据成员里面,则显而易见,使用 class C 的用户“看到”了不该“看到”的东西—— class X ——它们之间产生了耦合。然而,如果使用一个指向 class X 的指针,就可以将 X 的二进制信息“推”到 class C 的实现文件中去,在那里,我们 #include”x.hpp” ,定义所有的成员函数,并依赖于 X 的实现,这都无所谓,因为 C 的实现本来就依赖于 X ,重要的是:此时 class X 的改动只会导致 class C 的实现文件重新编译,而用户使用 class C 的源文件则安然无恙!
     指针在这里充当了一座桥。将依赖信息“推”到了另一个编译单元,与用户隔绝开来。而防火墙是 C++ 编译器的固有属性。
 
穿越C++编译期防火墙
是什么穿越了 C++ 编译期防火墙?是指针!使用指针的源文件“知道”指针所指的是什么对象,但是不必直接“看到”那个对象——它可能在另一个编译单元,是指针穿越了编译期防火墙,连接到了那个对象。
从某种意义上说,只要是代表地址的符号都能够穿越 C++ 编译期防火墙,而代表结构 (constructs) 的符号则不能。
     例如函数名,它指的是函数代码的始地址,所以,函数能够声明在一个编译单元,但定义在另一个编译单元,编译器会负责将它们连接起来。用户只要得到函数的声明就可以使用它。而类则不同,类名代表的是一个语言结构,使用类,必须知道类的定义,否则无法生成二进制代码。变量的符号实质上也是地址,但是使用变量一般需要变量的定义,而使用 extern 修饰符则可以将变量的定义置于另一个编译单元中。
【深入浅出 Yarn 架构与实现】4-5 RM 行为探究 - 启动 ApplicationMaster 本节开始,将对 ResourceManager 中一些常见行为进行分析探究,看某些具体关键的行为,在 RM 中是如何流转的。本节将深入源码探究「启动 ApplicationMaster」的具体流程。 阅读详情

相关推荐

【C++】PIMPL模式

一、PIMPL模式引入 PIMPL是指pointer to implementation,又称作“编译防火墙”。它通过将类B放置在单独的类A中,使用B的不透明指针进行访问实现,从而隐藏了A类的实现细节。是实现“将文件间的编译依存关系降至最低”的方法之一。 实例: 常规实现book类 book.h #pragma once #include <iostream> class book { public: book(std::string name,double price); ~

Jacky_Feng的博客 6276

什么是c++中的plmpl模

怎么使用呢?也就是本来要在一个类中声明对象的成员的,并且

曾经(一卡通/支付/卡系统/POS/读卡器 c/c++/java/c#/android/sql) 现在 c++/golang后台/window客户端高级开发 1747

C++程序设计机制——Pimpl机制

PImpl(private implementation) 其主要作用是解开类的使用接口和实现的耦合。

huangjh2017的博客 1581

旧话重提:pImpl惯用手法的背后

                       旧话重提:pImpl惯用手法的背后                                                  刘未鹏 pImpl惯用手法已经太老了,老得人们已经记不得它是什么时候被提出的了。像这么一个老得牙都掉了的东东几乎是肯定讲不出什么新意出来的。本文也不例外,只不过,这里我们并不想提出什么新的创意,而是对pI

刘未鹏|C++的罗浮宫 9108

单细胞分析(五)——使用Harmony进行数据整合和去批次

为了保证单细胞RNA测序数据的准确性和可靠性,需要对数据进行样本去批次处理。去批次的方法包括Harmony,SeuratV3等,这些方法可以有效地消除批次效应和技术噪声,并提高单细胞RNA测序数据的质量,为后续的数据分析提供更加可靠的基础。

阅读文献,记录学习笔记 3万+

TCP协议中ARQ自动重传的三种实现方式对比(含SACK详解)

本文深入解析TCP协议中ARQ自动重传的三种实现方式,包括停止-等待、回退N步和选择重传协议,特别详细介绍了SACK机制的工作原理和优势。通过对比分析各协议的效率、复杂度和适用场景,为网络通信中的可靠性传输提供了实用的选型指南。

weixin_30776273的博客 461

一文看懂Qt creator的ui文件设计及PIMPL原理

在Qt creator中,可以使用Qt Designer(Qt设计师)来快速设计界面,只需拖放就可以设计并快速浏览样式,并且可以生成代码,替代了用代码设计界面的工作。主要是生成了ui文件代替了用代码生成界面。那么这个过程是如何实现的呢? 以下是个简单的例子。新建了一个项目名,类名叫HelloDialog,派生自QDialog。在对话框上添加了一个按钮和一个文本标签。如下所示: 点击构建按钮会生成...

sksukai的博客 1548

ENSP从下载到运行:用虚拟机搭建华为认证实验环境全流程(含USG6000V镜像配置)

本文提供了一份详细的华为ENSP仿真平台从下载、安装到配置USG6000V防火墙镜像的完整指南。内容涵盖前置软件安装、ENSP主程序配置、核心设备镜像导入及虚拟机资源分配策略,旨在帮助网络工程师和学习者快速搭建一个稳定、功能齐全的华为认证实验环境,为备考和模拟企业网络部署打下坚实基础。

weixin_29230865的博客 533

PIMPL的原理 (理解 Qt namespace UI 前奏)

首先得弄清plmpl(实现私有化)的原理 城门失火殃及池鱼 pImpl惯用手法的运用方式大家都很清楚,其主要作用是解开类的使用接口和实现的耦合。如果不使用pImpl惯用手法,代码会像这样: //c.hpp #include class C { public: void f1(); private: X x; //与X的强耦合 }; 像上面这样的代码,C与它的实现

Keep moving…… 614

Gelsight Sensor 重建 height-map —— 使用 DST-II 算子的快速泊松求解

Gelsight Sensor 重建 height-map —— 使用 DST-II 算子的快速泊松求解 gelsight gelsight传感器具有软弹性体的接触表面,可以精确的重建物体形状还可以从传感器的变形推断接触力和滑动. 使用 DST-II 算子的快速泊松求解 height 文章:GelSight: High-Resolution Robot Tactile Sensors for Estimating Geometry and Force 文章最后求解的方程其实是: p=∂2u∂x2+∂

qq_42236622的博客 1240

vscode中使用PlatformIO创建工程加载慢

解决使用VSCODE+PlatformIO中,第一次新建工程一直加载的问题

Reasally的博客 4966

C++测试 pImpl的原理

城门失火殃及池鱼 pImpl惯用手法的运用方式大家都很清楚,其主要作用是解开类的使用接口和实现的耦合。如果不使用pImpl惯用手法,代码会像这样:        //c.hpp         #include class C         {         public:             void f1();         private:

Hedpat's Blog | 黯風夜隱 614

基于深度学习的农作物病虫害检测识别系统:YOLOv8 + UI界面 + 数据集

该系统的目标是使用YOLOv8模型自动检测农作物的病虫害,并通过UI界面提供实时反馈。用户可以通过上传图像,系统会输出图像中是否存在病虫害以及具体种类。数据集准备与处理:收集并准备包含农作物病虫害图像的数据集,并进行标注和数据增强。YOLOv8模型训练:使用YOLOv8训练农作物病虫害检测模型。UI界面设计与开发:开发一个用户友好的界面,支持上传图像并展示检测结果。系统测试与优化:评估模型的准确性、实时性,并对系统进行优化。

YOLO 882

C++编译防火墙:Pimpl模式原理、实践与性能优化详解

在C++大型项目开发中,编译依赖管理是提升工程效率的核心挑战之一。编译依赖指的是当某个头文件发生修改时,所有直接或间接包含它的源文件都需要重新编译,这在复杂项目中会导致漫长的编译等待时间,严重影响开发迭代速度。其根本原理在于C++的编译模型基于翻译单元,编译器需要确认类的内存布局和大小。为了解决这一问题,C++社区引入了编译防火墙技术,它通过将类的实现细节与接口分离,有效隔离了实现变动对编译范围的影响,从而显著减少增量编译时间,提升开发体验。这一技术的经典实现便是Pimpl模式(Pointer to IMP

dixi7825的博客 345

Python 爬虫进阶技巧:定时爬虫任务实现无人值守采集

常规爬虫多依赖手动触发脚本运行,单次采集完成后需人工二次启动,无法满足日常周期性数据监控、行情抓取、资讯同步、业务台账定时归档等常态化采集需求。搭建可自主调度、自动启停、周期循环的定时爬虫任务,脱离人工干预实现无人值守全自动采集,是爬虫从临时脚本走向工程化落地的关键一步。schedule 轻量定时库官方文档APScheduler 高级定时框架官方文档Python time 内置模块官方文档Linux crontab 定时任务官方手册。

2503_91057718的博客 1117

【知识图谱】实践篇——基于知识图谱的《红楼梦》人物关系可视化及问答系统实践:part2知识获取与图谱构建、服务搭建

前序文章:其中原项目提供了关系数据如下: 其中五列数据表示:人物1,人物2,人物1与人物2关系,人物1所属家族,人物2所属家族。关系说明:以第一列为例,贾演是贾代化的父亲。下面我们就将这个数据放到neo4j中。这些需要去连接,需要安装这个包: 程序中,我使用去读取配置文件, 其中为了保持配置全局唯一性,使用单例的写法。 后面就是创建构建关系文件,构建neo4j图谱数据了,如下: 后期可能会将构建图谱内容使用flask去生成命令行的方式构建,暂时在开发的过程查看构建情况,便于调试。导入关系后,我们再查看

让算法融入生活,改变生活! 4080

C++ Pimpl技法 C++接口实现分离原理

C++Pimpl技法 优点 将C++的接口和实现分离在两个类中 C++的实现和接口在不同的文件中,实现改变不会导致接口文件重新编译 将下面的代码分开在几个文件里面就实现了Pimpl技法 #include &lt;iostream&gt; class X; class C { public:     C();     virtual ~C();     void Fun(); ...

qq_28437139的博客 459

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

PIMPL 模式

接口类的头文件只包含公共接口声明和一个指向实现类的指针。这个指针用于在内部委托实现类执行具体操作。实现类包含具体的实现逻辑和私有数据。它只在.cpp文件中定义,通过 PIMPL 指针被接口类调用。

jjb-coder的博客 1441

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

Docmost 文档导入指南:4 种单文件格式 + 3 条批量迁移路径实操

周五下午接到任务:周末前把整个 Notion 工作区和旧 wiki 里攒了几年的 Markdown 技术文档,搬进团队新上的协作平台。逐页复制粘贴太慢,格式还容易乱。Docmost 的导入功能可以一步到位:单文件支持 Markdown、HTML、Docx、PDF;批量场景支持通用 ZIP 包和 Notion、Confluence 导出包,个人迁几篇文档也好,团队级知识库搬迁也好,都能覆盖。 ##

gitblog_00282的博客 1076

C++编程技巧之Pimpl手法

一、Pimpl是什么 Pimpl全称是point to implementation,译为指向实现的指针;是一种c++程序技巧。 其具有如下基本形式: class widget { // 其它部分 private: struct impl; // 前置申明一种变量类型 impl* pimpl_; // 定义该类型的指针或引用,用这个pimpl_来隐藏类widget的一些实现细节 }; 二、为什么需要Pimpl技术 我们知道,在C/C++中,声明变量是不占用

StarSky_Ye的博客 2279

【C++】Pimpl机制

【C++】Pimpl机制 1. 2.Pimpl简介 Pimpl的字面解释: Private Implementation 直译就是“实现私有化”。将实现私有化,使得头文件对改变不透明,解开类的使用接口和实现的耦合。 pointer to implementation “指向实现的指针”,这种说法更关注代码的实现方法,即创建一个opaque pointer指针,用以隐藏private成员。 Pimpl的思想是把类的公有使用接口与所有关于类的私有化实现分离。由于其他类使用该类的接口需要依赖类的头文件,头文件

weixin_44399394的博客 1770

什么是PIMPL?

转载自:https://www.cnblogs.com/joinclear/p/3908661.html      作者:joinclear     感谢 问题源自 陈硕《Linux多线程服务端编程》第十一章11.4节 可参考《C++编程规范》第43条 ----------------------------------------分隔----------------------------...

linkvivi的博客 2379

PIMPL IDIOM & FAST PIMPL

PIMPL是C++开发中经常使用的一种惯用法,其原理主要是将对定义的依赖转换为对声明的依赖,通过前向声明,达到接口与实现的分离的效果,并将编译时文件间的依赖降到最低,从而大大缩短程序编译的时间。PIMPL的实现方式,第一种是让实现类成为接口类的一个私有指针成员变量比如一个数据访问类DataAcce

lj_wml的专栏 419
上一篇: bezier 曲线的基本性质包括证明
下一篇: C++——模板特化和偏特化
Savitch
博客等级 码龄16年 84粉丝 130原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值