跟我学c++中级篇——Pimpl
一、前向声明
前向声明或者前置声明(forward declaration),这个在c++中用得还是比较多的。一般的框架或者库中,经常可以看到在一个类的前面声明了一个类,类似下面这样:
class useclass;
class mycall{...useclass *us;
};
前向声明,就是在应用这个类的某个类或者区域前声明一下,因此,被声明的类,对编译器来说是一个不完全的类型(incomplete type),它只是负责告诉编译器这个类型或者名称是有的,但没有这里(这和extern在这一点上有点类似)实现。所以编译如果遇到只是声明这个类的指针(或引用)的情况下,允许它编译通过。但是,在这种情况下是不允许直接操作这个类的内部一些特征的接口(函数或者变量)。换句话说,不允许定义这个类的对象,而只能受限使用即指针或引用以及用于声明为该类型做为形参或者返回值的函数。
它用在什么场景下?有什么用?举一个例子,如果有两个A和B,他们需要互相操作彼此,比如A向B写一个数,如果这个数达到一个值,就B就回写A一个值。这时候儿怎么办?如此A和B互相包含头文件,就是循环引用(当然,写在一起或者抽象一层,好吧)。这时候儿就需要一方不包含头文件,而使用这种前向声明,只在cpp文件中包含对方的头文件即可。
另外,前向声明可以解耦。还是A和B类,如果A类是一个接口类,但接口类中主要是操作B,那么,如果把B的头文件放出去就可以达到这个目的。但是,这样有几个问题,一个是可能泄露一些不想泄露的东西(特别某些算法里),另外一个,B类如果需要经常改变,那头文件也得老跟着变。这时候儿就可以把B搞成一个指针进行前向声明,具体的操作只在编译单元中进行。
而头文件的减少,好处还是比较多的,一个是降低了头文件include的顺序引起的莫名的问题,另外一个降低了编译时的依赖,提高了编译速度。
二、Pimpl
Pointer to implementation,也就是Pimpl,即通过指针指向实现而不是赤裸裸的把实现暴露出来。在侯捷老师《c++编程规范》和《Effective Modern c++》以及大牛陈硕的《c++工程实践经验谈》中都对使用PIMPL做为一种编译防火墙提高信息的隐藏度进行了分析说明以及各种工程实践的总结。
Pimpl其实就是使用一个私有的指针,来指向具体的需要隐藏的实现(可以简单理解为把原来接口类中的私有或保护成员抽象出来)。而具体的实现则通过外部接口类来操作这个指针来实现。由于在向外暴露的接口类头文件中只能看到这个私有指针的前向声明和指针声明,外部调用人员啥也看不到。它有几个好处:
1、隔离内外,形成一个安全区,即把错误可控的设计在范围内
2、减少二义性的出现。隐藏就意味着外部调用产生二义性的可能性被尽量隔绝
3、前向声明的好处,头文件的依赖减少并带来的编译开销的降低
4、对ABI有更好的兼容性
5、有可能使用延迟加载,提高资源的利用率
需要说明的是,不光c++可以使用这个技巧,C语言同样可以。
同样,有优点就会有缺点:
1、增加了复杂性,毕竟多一层抽象就多一层效率耗减,同时对指针的管理(new/delete)也增加了复杂性
2、需要处理拷贝(要么禁止掉)
3、const脱离了编译器的掌控,这种况下只能通过一些辅助的手段来达到目的
三、例程
下面看一个Pimpl的例子:
//PimplExample.h
#include <memory>
class PimplExample {
public:PimplExample();~PimplExample();int GetA();int GetB(int);int GetC();private:struct Impl;Impl *pimpl_;std::unique_ptr<Impl> ptr_;// std::shared_ptr<Impl> ptr_;
};
//打开注释,自己试试?
//PimplExample.cpp
#include <list>
#include <string>
struct PimplExample::Impl {int IGetA();int IGetB(int i) { return 0; };int d;std::list<int> l;
};PimplExample::PimplExample() : pimpl_(new Impl()) {}PimplExample::~PimplExample() { delete pimpl_; }int PimplExample::GetA()
{pimpl_->IGetA();
return 0;
}
int PimplExample::GetB(int i)
{pimpl_->IGetB(i);return 0;
}int PimplExample::Impl::IGetA()
{std::cout << "test" << std::endl;return 0;
}
//main.cpp
#include "PimplExample.h"
#include <iostream>
int main() {PimplExample mt;return 0;
}
这里面有一个小细节,如果把指针换成智能指针,试着用shared_ptr和unique_ptr来完成上面的代码,看看有什么问题没有?在实践中发现问题,解决问题,才是提升水平的一个重要手段。换成智能指针时,要把显示的析构函数注释掉,看看两种指针都会有啥现象。
如果想把程序写得更好一些,可以看pimpl类进一步封装成一个单独的类到文件中去,这样就和实际的工程应用相近了。
说Pimpl,其实和前向声明是密不可分的。可以理解为Pimpl是前向声明的一个应用场景(前向声明是Pimpl的泛型)。理解了一个技术的本质,就可以更好的应用这个技术并且屏蔽掉这个技术的副作用。仍然是举这个前向声明或者Pimpl的例子中,不是说Pimpl就包打一切,它其实是增加了复杂性,所以在非接口中,使用它就不一定是好的选择。另外,不把整个应用的层次搞清楚,就无法确定这个Pimpl应用在哪一层接口上更好。
即使如此,在实际的应用场景中,对于一些需要暴露的情况下,不一定非得把所有的成员都抽象到Pimpl的类中,包括处理虚拟函数也是如此。因此,到底如何更好的使用Pimpl需要根据实际情况来确定,不能简单的邯郸学步,有样学样。
四、总结
其实多读书,多实践对学计算机的人来说真得非常重要。很多人只看书,很少实践或者干脆反过来,结果就是进步太慢并且固步自封。同样,多读书,指的是多读精品的书籍而不是什么样的书都读(当然读优秀的代码也可以看成一种读书)。国内的书籍缺点往往是走两个极端,一个是学院派,只讲道理,实践很少或者干脆没有;或者是很多一线的人员写的书籍倾向于实战,理论不足。特别是理论和实践相结合的书籍更是少之又少,这也是往往推荐初学者去学习国外经典的原因。
这其实和国内的环境很有关系,在整个计算机的产业链上,国内仍然处于应用层,偶有底层建设也大多以国外开源为基础,完全原生少之又少。反倒是为某个语言优秀与否吵个沸反盈天,实属不智。这也是大环境使然,随着技术的发展,也许过一些年,这些东西就会补上来,未为可知。
Pimpl做为老生常谈,已经分析过几次了,这里再次补一篇!
相关文章:

跟我学c++中级篇——Pimpl
一、前向声明 前向声明或者前置声明(forward declaration),这个在c中用得还是比较多的。一般的框架或者库中,经常可以看到在一个类的前面声明了一个类,类似下面这样: class useclass; class mycall{...useclass *us; };前向声明…...
[补题记录] Atcoder Beginner Contest 295(E)
URL:https://atcoder.jp/contests/abc295 目录 E Problem/题意 Thought/思路 Code/代码 E Problem/题意 给定长度为 N 的数组 A。进行如下操作: 若 Ai 0,将 Ai 等概率地变为 1 ~ M 中的任意一个数;对 A 排序; …...

解决git在window11操作很慢,占用很大cpu的问题
【git在window11操作很慢,占用很大cpu,最后也执行失败】 在谷歌输入:git very slow in window 11。通过下面链接终于找到了解决方案: https://www.reddit.com/r/vscode/comments/sulebx/slow_git_in_wsl_after_updating_to_window…...

C++智能指针(二)——weak_ptr初探
文章目录 1. shared_ptr 存在的问题2. 使用weak_ptr2.1 初始化 weak_ptr2.2 访问数据 3. 附录4. 参考文献 1. shared_ptr 存在的问题 与 shared_ptr 的引入要解决普通指针存在的一些问题一样,weak_ptr 的引入,也是因为 shared_ptr 本身在某些情况下&…...

540 - Team Queue (UVA)
题目链接如下: Online Judge 对比刘汝佳的代码,我没有用queue来排整个队伍,因为那样的话遍历整个队伍太麻烦,vector比较方便。但vector删除元素比较耗时,所以就不删了,仅仅用pivot来指代目前队伍的开始。…...

投资组合之如何估值
文章目录 如何估值一、PE估值法1、PE估值法的定义2、参考标准(1)常规标准:25倍合理市盈率。(2)同行业对比。(3)跟历史市盈率相比。 3、PE估值法的适用范围4、PE估值法的优势5、PE估值法的劣势&a…...

2024届通信工程保研经验分享(预推免入营即offer)
2024届通信工程保研经验分享(预推免入营即offer) BackGround夏令营情况:预推免情况: BackGround 本科院校:末九 专业:通信工程 rank:3/123(预推免绩点排名)࿰…...

L2-025 分而治之 - java
L2-025 分而治之 时间限制 600 ms 内存限制 64 MB 题目描述: 分而治之,各个击破是兵家常用的策略之一。在战争中,我们希望首先攻下敌方的部分城市,使其剩余的城市变成孤立无援,然后再分头各个击破。为此参谋部提供了若…...

Python+高光谱数据预处理-机器学习-深度学习-图像分类-参数回归
涵盖高光谱遥感数据处理的基础、python开发基础、机器学习和应用实践。重点解释高光谱数据处理所涉及的基本概念和理论,旨在帮助学员深入理解科学原理。结合Python编程工具,专注于解决高光谱数据读取、数据预处理、高光谱数据机器学习等技术难题…...

免费 AI 编程助手 Amazon CodeWhisperer 体验
文章作者:文章作者:米菲爸爸 2022 年 6 月 23 亚马逊云科技就已经推出了 Amazon CodeWhisperer(预览版)。经过不到一年的测试和 AIGC的飓风在 2023 年 4 月 18 日实时 AI 编程助手 Amazon CodeWhisperer正式可用 Amazon CodeWhis…...

【Linux】从零开始学习Linux基本指令(一)
🚩纸上得来终觉浅, 绝知此事要躬行。 🌟主页:June-Frost 🚀专栏:Linux入门 🔥该文章主要了解Linux操作系统下的基本指令。 目录: ⌛️指令的理解⏳目录和文件的理解⏳一些常见指令✉…...

Java GC 算法
一、概述 理解Java虚拟机垃圾回收机制的底层原理,是成为一个高级Java开发者的基本功。本文从底层的垃圾回收算法开始,着重去阐释不同垃圾回收器在算法设计和实现时的一些技术细节,去探索「why」这一部分,通过对比不同的垃圾回收算…...

vue3 v-html中使用v-viewer
安装:npm install v-viewernext 在main.js中配置 import “viewerjs/dist/viewer.css”; import Viewer from “v-viewer”; app.use(Viewer, { Options: { inline: true, //默认值:false。启用内联模式。 button: true, //在查看器的右上角显示按钮。 …...

Leetcode算法解析——查找总价格为目标值的两个商品
1. 题目链接:LCR 179. 查找总价格为目标值的两个商品 2. 题目描述: 商品价格按照升序记录于数组 price。请在购物车中找到两个商品的价格总和刚好是 target。若存在多种情况,返回任一结果即可。 示例 1: 输入:price …...

unity游戏开发引擎unity3D开发
Unity(也被称为Unity3D)是一款强大的跨平台游戏引擎,用于开发2D和3D游戏,以及其他交互式应用程序。以下是Unity游戏开发的一般步骤: 安装和设置Unity: 首先,您需要下载并安装Unity。确保选择适…...

iptables
目录 iptables 匹配规则:由上到下依次匹配,一旦匹配不再匹配 参数 知识点 REJECT与DROP REJECT与DROP的区别 当使用的时REJECT时,客户端访问迅速返回的值是拒绝连接 当使用的是DROP时,返回的时连接超时 REJECT与drop适用…...

竞赛 深度学习LSTM新冠数据预测
文章目录 0 前言1 课题简介2 预测算法2.1 Logistic回归模型2.2 基于动力学SEIR模型改进的SEITR模型2.3 LSTM神经网络模型 3 预测效果3.1 Logistic回归模型3.2 SEITR模型3.3 LSTM神经网络模型 4 结论5 最后 0 前言 🔥 优质竞赛项目系列,今天要分享的是 …...

Spark入门
目录 Spark入门: 概述历史概述SparkCore:RDDSparkSQL:SparkStreamingSpark内核调优 Spark概述 回顾: Hadoop HDFS存储 MR分析计算 YARN调度 Hadoop的MR计算中的shuffle需要落盘,速度不够快。 Spark是一种基于内存的分析计算引擎。 历史…...

react–antd 实现TreeSelect树形选择组件,实现点开一层调一次接口
效果图: 注意: 当选择“否”,开始调接口,不要把点击调接口写在TreeSelect组件上,这样会导致问题出现,没有层级了 部分代码:...

android 固定进度环形刷新效果
android 固定进度无限旋转的环形效果 效果图 效果视频: Record_2023-10-13-17-17-19[1] Activity 中使用 val rotation: ObjectAnimator ObjectAnimator.ofFloat(progressBar, "rotation", 0f, 360f) rotation.duration 000 // 旋转持续时间为2秒 rot…...

python jieba 词性标注 中文词性分类 nlp jieba.posseg
参考:https://blog.csdn.net/yellow_python/article/details/83991967 from jieba.posseg import dt dt.word_tag_tab[好看] >>> vflag_en2cn { ‘a’: ‘形容词’, ‘ad’: ‘副形词’, ‘ag’: ‘形语素’, ‘an’: ‘名形词’, ‘b’: ‘区别词’, ‘…...

LeetCode 每日一题 2023/10/9-2023/10/15
记录了初步解题思路 以及本地实现代码;并不一定为最优 也希望大家能一起探讨 一起进步 目录 10/9 2578. 最小和分割10/10 2731. 移动机器人10/11 2512. 奖励最顶尖的 K 名学生10/12 2562. 找出数组的串联值10/13 1488. 避免洪水泛滥10/14 136. 只出现一次的数字10/1…...

相似性搜索:第 3 部分--混合倒排文件索引和产品量化
接续前文:相似性搜索:第 2 部分:产品量化 SImilarity 搜索是一个问题,给定一个查询的目标是在所有数据库文档中找到与其最相似的文档。 一、介绍 在数据科学中,相似性搜索经常出现在NLP领域,搜索引擎或推…...

小程序使用uni.createAnimation只执行一次的问题
思路: 在页面创建的时候,创建一个临时动画对象调用 step() 来表示一组动画完成通过动画实例的export方法导出动画数据传递给组件的animation属性还原动画页面卸载的时候,清除动画数据 <template><view class"content"&g…...

win10取消ie浏览器自动跳转edge浏览器
建议大家看完整篇文章再作操作 随着windows10 日渐更新,各种不同的操作,规避IE浏览器跳转Edge浏览器的问题 算了,找了台云机装的server 有自带的IE 1.(失败)思路 协助Edge浏览器 管理员身份打开 PowerShell 一般e…...

目录启示:使用 use 关键字为命名空间内的元素建立非限定名称
文章目录 参考环境三种名称非限定名称限定名称完全限定名称举个栗子 useuse 关键字use ... as .. 命名冲突真假美猴王两个世界 参考 项目描述搜索引擎Bing、GoogleAI 大模型文心一言、通义千问、讯飞星火认知大模型、ChatGPTPHP 官方PHP ManualPHP 官方language.namespaces.ra…...

Go语言介绍与安装
介绍与安装 本教程介绍了 Go,并讨论了选择 Go 相对于其他编程语言的优势。我们还将学习如何在Windows 中安装 Go。 介绍 Go也称为Golang,是由 Google 开发的一种开源、编译型、静态类型的编程语言。 Go创造背后的关键人物是Rob Pike、 Ken Thompson和…...
常用傅里叶变换表
傅里叶展开 傅里叶变换 傅里叶逆变换 时域信号 弧频域信号 线性变换 时域平移 频域平移 伸缩变换 微分性质 逆变换的微分性质 卷积定理 原函数变换结果 单位阶跃函数: 符号函数: 矩形函数: 辛格函数:...
生活中的视音频技术
生活中的视音频技术 平时我们打开电脑中自己存电影的目录的话,一般都会如下图所示,一大堆五花八门的电影。(其实专业的影视爱好者一概会把影视文件分门别类的,但我比较懒,一股脑把电影放在了一起) 因为下载…...

一种用于肽图分析的烷化剂,Desthiobiotin-Iodoacetamide
中文名:脱硫生物素-碘乙酰胺 英文名:Desthiobiotin-Iodoacetamide 化学式:C14H25IN4O3 分子量:424.28 外观:固体/粉末 规格:10mg、25mg、50mg等(接受各种规格的定制服务,具体可…...