PIMPL模式和D指针

目录

一、PIMPL模式概念:

1.1 pImpl手法的优势和目的 

1.1.1屏蔽实现细节

1.2 .2加速编译

1.2.3 更好的二进制兼容性

1.2.4 惰性分配

二、PIMPL模式项目中应用:

2.1 项目背景

2.2 对外接口代码:

2.2.1 对外库接口头文件

2.2.2 对外库接口部分实现:

三、D指针的应用:

3.1D指针的基本概念

3.2 D指针的使用举例


一、PIMPL模式概念:

        PIMPL(pointer to implementation, 指向实现的指针)是一种用来对“类的接口与实现”进行解耦合的方法。就是将真正的实现细节的Implementor从类定义的头文件中分离出去,公有类通过一个私有指针指向隐藏的实现类,是促进接口和实现分离的重要机制,可以避免在头文件中暴露私有细节。

        Pimpl 并不是严格意义上的设计模式(它是受制于 C++ 语言特定限制的变通方案),这种惯用法可以看作桥接设计模式的一种特例。

1.1 pImpl手法的优势和目的 

1.1.1屏蔽实现细节

        私有成员完全可以隐藏在共有接口之外,尤其对于闭源API的设计尤其的适合。此外,很多代码实现会依赖自身所在平台的宏等,这些琐碎的东西也不适合暴露给用户,避免造成干扰。

        在动态库项目中,设备提供的原生动态库往往不符合平台需求,需要添加一层接口去做适配,而不同设备有自身的sdk库和使用细节,这些对平台而言均是干扰项,此时使用pImpl手法接入中间适配层可以有效的屏蔽掉各个设备自身的实现细节,提供给平台一个干净简洁的接口文件。

1.2 .2加速编译

  这通常是用pImpl手法的最重要的收益,称之为编译防火墙(compilation firewall),主要是阻断了类的实现和类的实现两者的编译依赖性。这样,类用户不需要额外include不必要的头文件,同时实现类的成员可以随意变更,而公有类的使用者不需要重新编译。

        在实际项目编译时间优化时,对于类似common.h的超大头文件除了按照功能等标准进行拆分外,更近一步对拆分的类的使用方法上可以使用pImpl手法,避免这些类的波动导致用这些类的用户类重新编译。

1.2.3 更好的二进制兼容性

  通常对一个类的修改,会影响到类的大小、对象的表示和布局等信息,那么任何该类的用户都需要重新编译才行。而对于使用pImpl手法,如果实现变更被限制在实现类中,那公有类只持有一个实现类的指针,所以实现做出重大变更的情况下,pImpl也能够保证良好的二进制兼容性。

1.2.4 惰性分配

  实现类可以做到按需分配或者实际使用时候再分配,从而节省资源提高响应。

二、PIMPL模式项目中应用:

2.1 项目背景

        在高速公路收费站项目中,一般收费平台需要接入称重的地磅系统、车牌车型等识别的智能相机识别系统、ETC和天线扣费系统以及道闸管控系统等;对于此类项目各个省市的设备均有所不同,同一省市项目中也可能多家设备同时接入共同联调。本项目是以智能相机识别系统为背景,对于不同项目采购的不同类型识别相机进行平台动态库接口的封装,由于平台接口基本一致,但是各种类型设备却各有特点,因此采用Pimip模式屏蔽具体的设备接入部分,提供干净的统一接口给高速收费系统平台。

2.2 对外接口代码:

2.2.1 对外库接口头文件
#pragma once
#define WINAPI __cdecl
#define  API   __declspec(dllexport)
#include <windows.h>
#include <memory>enum VPROPT_TYPE
{OPT_VEHICLEINFO,OPT_REVIEWINFO
};typedef struct
{int			ID;						//ID号//车头车牌信息///char		plateStr[20];			//车头车牌号码,可能是空或无牌车char		plateColor[5];			//车头车牌颜色,包括蓝、白、黑、黄、绿、无//车尾车牌信息///char		tailPlateNump[20];		//车尾车牌号码char		tailPalteColor[5];		//车尾车牌颜色//车型信息int			carType;				//车型结果[数值型],客车:1-4, (没有给11),货车:11-16,专项作业:21-26char		carTypeStr[64];			//车型结果[字符串],客1-客4,货1-货6,专1-专6 (没有给货1) int			szZhoushu;				//轴数(没有给0)int			szLunshu;				//轮数(没有给0)//图片和视频//char		colpic[260];			//车辆侧面图绝对路径char		headpic[260];			//车头车牌图绝对路径char		tailpic[260];			//车尾车牌图绝对路径char		recfile[260];			//录像文件绝对路径char		platepic[260];			//车头车牌小图绝对路径char		plateBin[260];			//车头车牌二值图图绝对路径
}SCarInfoResult;typedef struct
{HWND		hWnd;					//窗口句柄int			msgNo;					//自定义消息
}MessageCallBackInfo;typedef int(*VehRec_GetCarDataFun)(int handle, SCarInfoResult pCarData);typedef unsigned int VPR_HANDLE;//对外接口类 与库文件一起提供给平台
class VehRecongize
{
public:VehRecongize(char *iLogPath);//动态库初始化API int WINAPI VehRec_Init();//动态库资源释放API int WINAPI VehRec_Free();//设备连接API int WINAPI VehRec_Connect(char *devIP, char *savepath);//设备断开API void WINAPI VehRec_DisConnect(int handle);//设置抓拍回调函数API int WINAPI VehRec_SetCarDataFun(int handle, VehRec_GetCarDataFun pCallBack);//注册序号API int WINAPI VehRec_RegisterMessage(int handle, HWND hWnd, int msgNo);//获取车辆抓拍信息API int WINAPI VehRec_GetCarInfo(int handle, SCarInfoResult *pCarInfoRes);//校验相机状态API int WINAPI VehRec_CheckStatus(int handle, char* pStatus);//开始视频播放API int WINAPI  VehRec_StartDisplay(int handle, int nWidth, int nHeight, int type, int nWinid);//结束视频播放API int WINAPI  VehRec_StopDisplay(int handle, int nWinid);//时间同步API int WINAPI VehRec_SyncTime(int handle, char* sSyncTime);//手动抓拍API BOOL WINAPI  VehRec_Capture(int handle);//字符叠加API BOOL WINAPI  VehRec_AddCharacter(int handle,char*character);
private://具体camera的实现类可封装在CameraApiImplement中,此处以指针形式声明和调用具体设备接口//达到对调用改库平台关于具体设备的细节屏蔽,提供干净的接口类class CameraApiImplement;std::unique_ptr<CameraApiImplement> pImip;
};
2.2.2 对外库接口部分实现:

        这里只写了库的初始化、释放和设备连接的关键实现部分;对于库日志等共有的没必要在pimip中封装,只需将设备相关的功能接口和设备管理等进行封装到pimip即可,此处对外接口类即可实现基本不变,只需根据具体的设备不同修改pimip中的设备相关部分代码即可。

	#include"vehRecongizeDll.h"//实际设备相关的业务均屏蔽在pImip实现里面 此处调用pImip指针即可//pImip抽象出通用的接口 具体不同设备的实现隐藏在pImip的实现类里面VehRecongize::VehRecongize(char *iLogPath):pImip(std::make_unique<CameraApiImplement>()){}API int WINAPI VehRecongize::VehRec_Init(){if (!g_logger) { g_logger = new Logger(iLogPath); }log_sprintf(*g_logger, "VehRec_InitEx iLogPath=%s", iLogPath);if (!pImip.g_DevMag){pImip.g_DevMag = new DeviceManager(*g_logger, 3);}if (!pImip.g_DevMag->isSdkInit()){return -3;}return 0;}API int WINAPI VehRecongize::VehRec_Free(){log_sprintf(*g_logger, "Enter VehRec_Free ");if (!pImip.g_DevMag){return -99;}if (!pImip.g_DevMag->CameraAllLogoutAndCloseAlarm()){log_sprintf(*g_logger, "VehRec_Free error.  %s", __func__);return -1;}if (pImip.g_DevMag) { delete pImip.g_DevMag; pImip.g_DevMag = nullptr; }if (pImip.g_iniFile) { delete pImip.g_iniFile; pImip.g_iniFile = nullptr; }if (g_logger) { delete g_logger; g_logger = nullptr; }return 0;}API int WINAPI VehRecongize::VehRec_Connect(char *devIP, char *savepath){if (!g_logger) { g_logger = new Logger(); }if (devIP == NULL || savepath == NULL){log_sprintf(*g_logger, "VehRec_Connect param error!");return -1;}log_sprintf(*g_logger, "Enter VehRec_Connect! devIP %s, savepath %s", devIP, savepath);if (!g_iniFile) {std::string path = GetModuleLocalPath("VehRecDll.dll");if (path.empty()){log_sprintf(*g_logger, "iniFile path empty");return -1;}path.append("Config.ini");g_iniFile = new IniFile(path.c_str());if (!g_iniFile->IsOpen()){log_sprintf(*g_logger, "iniFile open failure %s", path.c_str());delete g_iniFile;g_iniFile = NULL;return -1;}}int handle = -1;if (g_iniFile->IsOpen()){std::string m_strUser;std::string m_strPassword;DWORD m_nPort;// read camera configm_strUser = g_iniFile->readstring("DETECTOR", "User", "admin");m_strPassword = g_iniFile->readstring("DETECTOR", "Password", "abc12345");m_nPort = (DWORD)g_iniFile->readinteger("DETECTOR", "Port", 8000);if (!pImip.g_DevMag){pImip.g_DevMag = new DeviceManager(*g_logger, 3);}if (handle = pImip.g_DevMag->CameraLoginAndAlarm(m_strUser.c_str(), m_strPassword.c_str(), devIP, m_nPort), handle < 1000){log_sprintf(*g_logger, "VPR_Init Device failed IP %s", devIP);return -4;}}else{log_sprintf(*g_logger, "IniFile not open %s", __func__);return -1;}pImip.g_DevMag->setSaveDir(savepath);return handle;}//...

三、D指针的应用:

3.1D指针的基本概念

      D指针(也被称为P指针或d指针)是Qt中Pimp模式的一种实现,用于隐藏类的实现细节,从而实现更好的封装性和二进制兼容性。它特别用于Qt中的许多核心类,以允许在不破坏二进制兼容性的情况下进行重大更改。 基本思路是这样的:每个公开的Qt类(例如QWidget)都有一个私有的实现类(例如QWidgetPrivate),并且这个公开类含有一个指向其私有实现类的指针(即D指针)。这样,大部分类的成员变量和函数都隐藏在这个私有实现类中,而公开的Qt类只包含一些基本的、稳定的接口。因此,Qt的版本升级中可以保证 在不破坏现有API的情况下添加新的功能或优化性能。

        在Qt源代码中,D指针通常命名为d_ptr或d,并通过Q_D宏来访问。这个宏会检查D指针是否非空,并返回它的值。如果D指针为空(例如在对象的构造函数中),则Q_D宏会返回一个临时对象,该对象会触发断言,以防止访问未初始化的成员。 使用D指针是Qt实现其强大功能和稳定性的关键之一。这种设计模式并不只限于Qt,它在许多其他大型C++项目中也很常见。

3.2 D指针的使用举例

D指针涉及到的宏:

使用举例

        可以看出Q纸质就是PIMPI的一种使用方式,只是通过宏进行包裹,在Qt中可以定式方便使用而已。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/479378.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Android显示系统(01)- 架构分析

一、前言&#xff1a; Android是基于Linux的&#xff0c;而显示设备的驱动也都是和Linux普通设备一样去管理&#xff0c;也就是说归根结底还是要按照Linux的方式组织数据送给LCD&#xff0c;因此&#xff0c;我们理解Android设计的这一套复杂的显示系统时候&#xff0c;一定要…

15分钟做完一个小程序,腾讯这个工具有点东西

我记得很久之前&#xff0c;我们都在讲什么低代码/无代码平台&#xff0c;这个概念很久了&#xff0c;但是&#xff0c;一直没有很好的落地&#xff0c;整体的效果也不算好。 自从去年 ChatGPT 这类大模型大火以来&#xff0c;各大科技公司也都推出了很多 AI 代码助手&#xff…

跨标签通信的几种方式

以前面试被问到过&#xff0c;就了解了一下。还有其他方式&#xff0c;但是实际开发中&#xff0c;使用第一个就可以了 目录 1. 使用BroadcastChannel 2. 使用SharedWorker 3. 使用webSocket 1. 使用BroadcastChannel 它允许同源&#xff08;协议、域名、端口都相同&#x…

深度神经网络模型压缩学习笔记二:离线量化算法和工具、实现原理和细节

文章目录 一、离线量化基础概念1&#xff09;离线量化定义2&#xff09;离线量化优缺点3&#xff09;如何生产一个硬件能跑的量化模型&#xff1f;4&#xff09;离线量化的类型5&#xff09;如何计算scale&#xff0c;zero_point?6&#xff09;离线量化概念7&#xff09;PTQ与…

HTML详解(1)

1.HTML定义 HTML&#xff1a;超文本标记语言。超文本&#xff1a;通过链接可以把多个网页链接到一起标记&#xff1a;标签&#xff0c;带括号的文本后缀&#xff1a;.html 标签语法&#xff1a;<strong>需加粗文字</strong> 成对出现&#xff0c;中间包裹内容&l…

【21-30期】Java技术深度剖析:从分库分表到微服务的核心问题解析

&#x1f680; 作者 &#xff1a;“码上有前” &#x1f680; 文章简介 &#xff1a;Java &#x1f680; 欢迎小伙伴们 点赞&#x1f44d;、收藏⭐、留言&#x1f4ac; 文章题目&#xff1a;Java技术深度剖析&#xff1a;从分库分表到微服务的核心问题解析 摘要&#xff1a; 本…

Jmeter中的配置原件

2&#xff09;配置原件 1--CSV Data Set Config 用途 参数化测试&#xff1a;从CSV文件中读取数据&#xff0c;为每个请求提供不同的参数值。数据驱动测试&#xff1a;使用外部数据文件来驱动测试&#xff0c;使测试更加灵活和可扩展。 配置步骤 准备CSV文件 创建一个CSV文…

Redis持久化、主从及哨兵架构详解

Redis持久化 RDB快照&#xff08;snapshot&#xff09; 在默认情况下&#xff0c;Redis将内存数据库快照保存在名字为dump.rdb的二进制文件中。 你可以对Redis进行设置&#xff0c;让它在“N秒内数据集至少有M个改动”这一条件被满足时&#xff0c;自动保存一次数据集。 比…

双向链表、循环链表、栈

双向循环链表 class Node:#显性定义出构造函数def __init__(self,data):self.data data #普通节点的数据域self.next None #保存下一个节点的链接域self.prior None #保存前一个节点饿链接域 class DoubleLinkLoop:def __init__(self, node Node):self.head nodeself.siz…

【青牛科技】D1671 75Ω 带4级低通滤波的单通道视频放大电 路芯片介绍

概 述 &#xff1a; D1671是 一 块 带 4级 低 通 滤 波 的 单 通 道 视 频 放 大 电 路 &#xff0c; 可 在3V或5V的 低 电 压 下 工 作 。 该 电 路 用 在 有 TV影 象 输 出 功 能 的 产 品 上 面&#xff0c;比如 机 顶 盒 &#xff0c;监 控 摄 象 头 &#xff0c;DVD&#…

Linux服务器生成SSH 密钥对与 GitLab 仓库进行交互

目录 生成 SSH 密钥对 将公钥添加到 GitLab 测试 SSH 连接 生成 SSH 密钥对 在执行脚本的机器上打开终端&#xff0c;执行以下命令&#xff08;假设使用默认的 RSA 算法&#xff0c;一路回车使用默认设置即可&#xff0c;也可以根据需要指定其他算法和参数&#xff09;&…

关于SpringBoot集成Kafka

关于Kafka Apache Kafka 是一个分布式流处理平台&#xff0c;广泛用于构建实时数据管道和流应用。它能够处理大量的数据流&#xff0c;具有高吞吐量、可持久化存储、容错性和扩展性等特性。 Kafka一般用作实时数据流处理、消息队列、事件架构驱动等 Kafka的整体架构 ZooKeeper:…

在Unity中实现物体动画的完整流程

在Unity中&#xff0c;动画是游戏开发中不可或缺的一部分。无论是2D还是3D游戏&#xff0c;动画都能为游戏增添生动的视觉效果。本文将详细介绍如何在Unity中为物体添加动画&#xff0c;包括资源的准备、播放组件的添加、动画控制器的创建以及动画片段的制作与调度。 1. 准备动…

自定义协议

1. 问题引入 问题&#xff1a;TCP是面向字节流的&#xff08;TCP不关心发送的数据是消息、文件还是其他任何类型的数据。它简单地将所有数据视为一个字节序列&#xff0c;即字节流。这意味着TCP不会对发送的数据进行任何特定的边界划分&#xff0c;它只是确保数据的顺序和完整…

Spring Boot 3.4.0 发行:革新与突破的里程碑

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家&#xff0c;历代文学网&#xff08;PC端可以访问&#xff1a;https://literature.sinhy.com/#/literature?__c1000&#xff0c;移动端可微信小程序搜索“历代文学”&#xff09;总架构师&#xff0c;15年工作经验&#xff0c;…

android 11添加切换分屏功能

引言 自Android 7开始官方就支持分屏显示,但没有切换分屏的功能,即交换上下屏幕。直到Android 13开始才支持切换分屏,操作方式是:分屏模式下双击中间分割线就会交换上下屏位置。本文的目的就是在Android 11上实现切换分屏的功能。 下图是Android13切换分屏演示 切换分屏…

PyTorch基础05_模型的保存和加载

目录 一、模型定义组件——重构线性回归 二、模型的加载和保存 2、序列化保存对象和加载 3、保存模型参数 一、模型定义组件——重构线性回归 回顾之前的手动构建线性回归案例&#xff1a; 1.构建数据集&#xff1b;2.加载数据集(数据集转换为迭代器)&#xff1b;3.参数初…

JavaScript核心语法(3)

前两篇文章大概把JavaScript的基础语法讲了一下&#xff0c;这篇文章主要讲讲ES6的核心语法。ES6的核心语法说实话其实有点多&#xff0c;我重点挑一些经常在项目中用到的来讲&#xff0c;其他一些我没怎么见过的就不讲了。 目录 1.变量和常量 变量&#xff08;let 和 var&a…

爬虫开发(5)如何写一个CSDN热门榜爬虫小程序

笔者 綦枫Maple 的其他作品&#xff0c;欢迎点击查阅哦~&#xff1a; &#x1f4da;Jmeter性能测试大全&#xff1a;Jmeter性能测试大全系列教程&#xff01;持续更新中&#xff01; &#x1f4da;UI自动化测试系列&#xff1a; SeleniumJava自动化测试系列教程❤ &#x1f4da…

NIO三大组件

现在互联网环境下&#xff0c;分布式系统大相径庭&#xff0c;而分布式系统的根基在于网络编程&#xff0c;而netty恰恰是java领域的网络编程的王者&#xff0c;如果要致力于并发高性能的服务器程序、高性能的客户端程序&#xff0c;必须掌握netty网络编程。 NIO基础 NIO是从ja…