D-Pointer(Pimpl)设计模式(指向实现的指针)

Qt 的 D-Pointer(Pimpl)设计模式

1. Pimpl 模式简介

Pimpl(Pointer to Implementation)是一种设计模式,用于将类的接口与实现分离,从而隐藏实现细节,降低编译依赖,提高代码的可维护性和可扩展性。这种模式在 Qt 中被称为 D-Pointer,广泛应用于 Qt 框架中。

2. Pimpl 模式的优势
  • 隐藏实现细节:通过将私有成员变量和方法放在一个单独的类中,头文件中只包含一个指向该类的指针,从而隐藏了实现细节。
  • 降低编译依赖:修改私有实现不会影响到头文件,因此不会引起重新编译。
  • 提高编译速度:由于头文件中只包含一个指针,编译依赖减少,编译速度提升。
  • 保持稳定的 ABI:即使内部实现发生变化,只要接口保持不变,二进制接口(ABI)也保持稳定。
3. Qt 中的 D-Pointer 实现

在 Qt 中,D-Pointer 的实现主要通过以下宏来完成:

  • Q_DECLARE_PRIVATE:在公共类中声明一个指向私有实现类的指针。
  • Q_DECLARE_PUBLIC:在私有实现类中声明一个指向公共类的指针。
  • Q_DQ_Q:在公共类和私有实现类中分别用于访问对方。
4. 实现示例
公共类 ElaApplication
#include <QObject>
#include "ElaApplicationPrivate.h"  // 包含私有实现类的声明class ElaApplication : public QObject {Q_OBJECTQ_DECLARE_PRIVATE(ElaApplication)  // 声明私有实现类指针
public:explicit ElaApplication(QObject *parent = nullptr);~ElaApplication();void setIsEnableMica(bool enable);bool isEnableMica() const;void setMicaImagePath(const QString &path);QString micaImagePath() const;private:ElaApplicationPrivate *d_ptr;  // 指向私有实现类的指针
};
私有实现类 ElaApplicationPrivate
#include "ElaApplication.h"class ElaApplicationPrivate {Q_DECLARE_PUBLIC(ElaApplication)  // 声明公共类指针
public:ElaApplicationPrivate(ElaApplication *q);~ElaApplicationPrivate();bool isEnableMica;QString micaImagePath;private:ElaApplication *q_ptr;  // 指向公共类的指针
};
实现文件 ElaApplication.cpp
#include "ElaApplication.h"
#include "ElaApplicationPrivate.h"ElaApplication::ElaApplication(QObject *parent): QObject(parent), d_ptr(new ElaApplicationPrivate(this)) {Q_D(ElaApplication);  // 使用 Q_D 宏获取私有实现指针d->isEnableMica = false;  // 初始化私有成员
}ElaApplication::~ElaApplication() {delete d_ptr;  // 删除私有实现
}void ElaApplication::setIsEnableMica(bool enable) {Q_D(ElaApplication);  // 使用 Q_D 宏获取私有实现指针d->isEnableMica = enable;
}bool ElaApplication::isEnableMica() const {Q_D(const ElaApplication);  // 使用 Q_D 宏获取私有实现指针return d->isEnableMica;
}void ElaApplication::setMicaImagePath(const QString &path) {Q_D(ElaApplication);  // 使用 Q_D 宏获取私有实现指针d->micaImagePath = path;
}QString ElaApplication::micaImagePath() const {Q_D(const ElaApplication);  // 使用 Q_D 宏获取私有实现指针return d->micaImagePath;
}
5. 总结
  • Pimpl 模式:通过将实现细节隐藏在私有类中,减少编译依赖,提高代码的可维护性和可扩展性。
  • Qt 的 D-Pointer:通过 Q_DECLARE_PRIVATEQ_DECLARE_PUBLICQ_D 等宏,Qt 提供了一种简洁的方式来实现 Pimpl 模式。
  • 应用场景:Pimpl 模式在需要隐藏实现细节、减少编译依赖和保持稳定的 ABI 的场景中非常有用。

希望这些信息能帮助你更好地理解 Qt 的 D-Pointer(Pimpl)设计模式!


示例代码

公共类 Car 的头文件
#include <QObject>class CarPrivate;  // 前置声明私有类class Car : public QObject {Q_OBJECT// Q_DECLARE_PRIVATE 宏展开// 原始宏定义:// #define Q_DECLARE_PRIVATE(Class) \//     inline Class##Private* d_func() { return reinterpret_cast<Class##Private *>(qGetPtrHelper(d_ptr)); } \//     inline const Class##Private* d_func() const { return reinterpret_cast<const Class##Private *>(qGetPtrHelper(d_ptr)); } \//     friend class Class##Private;inline CarPrivate* d_func() { return reinterpret_cast<CarPrivate *>(qGetPtrHelper(d_ptr)); }  // 获取非const的私有实现inline const CarPrivate* d_func() const { return reinterpret_cast<const CarPrivate *>(qGetPtrHelper(d_ptr)); }  // 获取const的私有实现friend class CarPrivate;  // 声明私有实现类为友元类public:Car(QObject *parent = nullptr);~Car();void startEngine();  // 公共方法private:CarPrivate *d_ptr;  // 指向私有实现类的指针
};
私有实现类 CarPrivate 的头文件
#include "Car.h"class CarPrivate {// Q_DECLARE_PUBLIC 宏展开// 原始宏定义:// #define Q_DECLARE_PUBLIC(Class) \//     Class *q_ptr; \//     inline Class *q_func() { return q_ptr; } \//     inline const Class *q_func() const { return q_ptr; }Car *q_ptr;  // 指向公共类的指针inline Car *q_func() { return q_ptr; }  // 获取非const的公共类指针inline const Car *q_func() const { return q_ptr; }  // 获取const的公共类指针public:CarPrivate(Car *q) : q_ptr(q) {}bool engineStarted = false;  // 私有成员变量
};
实现文件 Car.cpp
#include "Car.h"
#include "CarPrivate.h"// 构造函数
Car::Car(QObject *parent) : QObject(parent), d_ptr(new CarPrivate(this)) {// 初始化私有类
}// 析构函数
Car::~Car() {delete d_ptr;  // 删除私有类
}// startEngine 方法
void Car::startEngine() {// Q_D 宏展开// 原始宏定义:// #define Q_D(Class) Class##Private *const d = d_func()CarPrivate *const d = d_func();  // 获取私有实现指针d->engineStarted = true;  // 修改私有成员变量
}

详细解释

1. Q_DECLARE_PRIVATE
// 原始宏定义:
// #define Q_DECLARE_PRIVATE(Class) \
//     inline Class##Private* d_func() { return reinterpret_cast<Class##Private *>(qGetPtrHelper(d_ptr)); } \
//     inline const Class##Private* d_func() const { return reinterpret_cast<const Class##Private *>(qGetPtrHelper(d_ptr)); } \
//     friend class Class##Private;inline CarPrivate* d_func() { return reinterpret_cast<CarPrivate *>(qGetPtrHelper(d_ptr)); }  // 获取非const的私有实现
inline const CarPrivate* d_func() const { return reinterpret_cast<const CarPrivate *>(qGetPtrHelper(d_ptr)); }  // 获取const的私有实现
friend class CarPrivate;  // 声明私有实现类为友元类
  • d_func():提供了一个方法来访问私有实现类的指针。
  • friend class CarPrivate:将私有实现类声明为友元类,允许它访问公共类的私有成员。
2. Q_DECLARE_PUBLIC
// 原始宏定义:
// #define Q_DECLARE_PUBLIC(Class) \
//     Class *q_ptr; \
//     inline Class *q_func() { return q_ptr; } \
//     inline const Class *q_func() const { return q_ptr; }Car *q_ptr;  // 指向公共类的指针
inline Car *q_func() { return q_ptr; }  // 获取非const的公共类指针
inline const Car *q_func() const { return q_ptr; }  // 获取const的公共类指针
  • q_ptr:在私有实现类中声明一个指向公共类的指针。
  • q_func():提供了一个方法来访问公共类的指针。
3. Q_D
// 原始宏定义:
// #define Q_D(Class) Class##Private *const d = d_func()CarPrivate *const d = d_func();  // 获取私有实现指针
  • Q_D:简化代码,通过 d_func() 获取私有实现类的指针,并将其存储在局部变量 d 中。

完整的示例代码

公共类 Car 的头文件
#include <QObject>class CarPrivate;  // 前置声明私有类class Car : public QObject {Q_OBJECT// Q_DECLARE_PRIVATE 宏展开inline CarPrivate* d_func() { return reinterpret_cast<CarPrivate *>(qGetPtrHelper(d_ptr)); }  // 获取非const的私有实现inline const CarPrivate* d_func() const { return reinterpret_cast<const CarPrivate *>(qGetPtrHelper(d_ptr)); }  // 获取const的私有实现friend class CarPrivate;  // 声明私有实现类为友元类public:Car(QObject *parent = nullptr);~Car();void startEngine();  // 公共方法private:CarPrivate *d_ptr;  // 指向私有实现类的指针
};
私有实现类 CarPrivate 的头文件
#include "Car.h"class CarPrivate {// Q_DECLARE_PUBLIC 宏展开Car *q_ptr;  // 指向公共类的指针inline Car *q_func() { return q_ptr; }  // 获取非const的公共类指针inline const Car *q_func() const { return q_ptr; }  // 获取const的公共类指针public:CarPrivate(Car *q) : q_ptr(q) {}bool engineStarted = false;  // 私有成员变量
};
实现文件 Car.cpp
#include "Car.h"
#include "CarPrivate.h"// 构造函数
Car::Car(QObject *parent) : QObject(parent), d_ptr(new CarPrivate(this)) {// 初始化私有类
}// 析构函数
Car::~Car() {delete d_ptr;  // 删除私有类
}// startEngine 方法
void Car::startEngine() {// Q_D 宏展开CarPrivate *const d = d_func();  // 获取私有实现指针d->engineStarted = true;  // 修改私有成员变量
}

总结

  • d_ptr:通过 Q_DECLARE_PRIVATE 宏声明,是一个指向私有实现类的指针。
  • d_func():通过 Q_DECLARE_PRIVATE 宏定义,用于获取私有实现类的指针。
  • Q_D:简化代码,通过 d_func() 获取私有实现类的指针,并存储在局部变量 d 中。

1. Q_DECLARE_PRIVATE 宏的定义

Q_DECLARE_PRIVATE 宏的定义如下:

#define Q_DECLARE_PRIVATE(Class) \inline Class##Private* d_func() { return reinterpret_cast<Class##Private *>(qGetPtrHelper(d_ptr)); } \inline const Class##Private* d_func() const { return reinterpret_cast<const Class##Private *>(qGetPtrHelper(d_ptr)); } \friend class Class##Private;

2. 宏的展开

假设我们有一个类 Car,其私有实现类为 CarPrivate。使用 Q_DECLARE_PRIVATE(Car) 宏后,代码会展开为:

inline CarPrivate* d_func() { return reinterpret_cast<CarPrivate *>(qGetPtrHelper(d_ptr)); }  // 获取非const的私有实现
inline const CarPrivate* d_func() const { return reinterpret_cast<const CarPrivate *>(qGetPtrHelper(d_ptr)); }  // 获取const的私有实现
friend class CarPrivate;  // 声明私有实现类为友元类

3. d_ptr 的作用

Q_DECLARE_PRIVATE 宏中,d_ptr 是一个特定的变量名,它被用来存储指向私有实现类的指针。Q_DECLARE_PRIVATE 宏生成的代码依赖于 d_ptr,因为它假设类中有一个名为 d_ptr 的成员变量。

4. 如果将 d_ptr 改为 fadsfdas_ptr

如果你将 d_ptr 改为 fadsfdas_ptrQ_DECLARE_PRIVATE 宏生成的代码将无法正确工作,因为宏生成的代码仍然会尝试访问 d_ptr,而不是 fadsfdas_ptr

修改后的代码
class Car : public QObject {Q_OBJECT// Q_DECLARE_PRIVATE 宏展开inline CarPrivate* d_func() { return reinterpret_cast<CarPrivate *>(qGetPtrHelper(d_ptr)); }  // 获取非const的私有实现inline const CarPrivate* d_func() const { return reinterpret_cast<const CarPrivate *>(qGetPtrHelper(d_ptr)); }  // 获取const的私有实现friend class CarPrivate;  // 声明私有实现类为友元类public:Car(QObject *parent = nullptr);~Car();void startEngine();  // 公共方法private:CarPrivate *fadsfdas_ptr;  // 指向私有实现类的指针
};
问题
  • d_func() 方法仍然尝试访问 d_ptr,但 d_ptr 不存在。
  • Q_DECLARE_PRIVATE 宏生成的代码依赖于 d_ptr,而不是 fadsfdas_ptr

5. 自定义宏

如果你需要使用不同的变量名(例如 fadsfdas_ptr),你需要手动实现类似 Q_DECLARE_PRIVATE 的功能。这可以通过自定义宏来完成,但不推荐,因为这会增加代码的复杂性和维护难度。

自定义宏
#define Q_DECLARE_PRIVATE_EX(Class, PtrName) \inline Class##Private* d_func() { return reinterpret_cast<Class##Private *>(qGetPtrHelper(PtrName)); } \inline const Class##Private* d_func() const { return reinterpret_cast<const Class##Private *>(qGetPtrHelper(PtrName)); } \friend class Class##Private;class Car : public QObject {Q_OBJECTQ_DECLARE_PRIVATE_EX(Car, fadsfdas_ptr)  // 使用自定义宏public:Car(QObject *parent = nullptr);~Car();void startEngine();  // 公共方法private:CarPrivate *fadsfdas_ptr;  // 指向私有实现类的指针
};

6. 详细说明

(1)Q_DECLARE_PRIVATE 宏的依赖

Q_DECLARE_PRIVATE 宏依赖于 d_ptr,因为它假设类中有一个名为 d_ptr 的成员变量。这个变量名是固定的,宏生成的代码会直接使用它。

(2)d_func() 方法

d_func() 方法通过 qGetPtrHelper(d_ptr) 提取 d_ptr 的原始指针,并将其转换为 CarPrivate * 类型。如果 d_ptr 不存在,d_func() 方法将无法正确工作。

(3)friend class CarPrivate

friend class CarPrivate 声明私有实现类为友元类,允许它访问公共类的私有成员。这个声明与 d_ptr 的名字无关,但它是 Q_DECLARE_PRIVATE 宏的一部分。

7. 总结

  • Q_DECLARE_PRIVATE 宏依赖于 d_ptr:它生成的代码假设类中有一个名为 d_ptr 的成员变量。
  • 改变变量名会导致问题:如果将 d_ptr 改为其他名称(例如 fadsfdas_ptr),宏生成的代码将无法正确工作。
  • 推荐使用默认的 d_ptr:为了保持代码的一致性和可维护性,建议使用默认的 d_ptr

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

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

相关文章

MySQL 8.0 OCP 1Z0-908 101-110题

Q101.which two queries are examples of successful SQL injection attacks? A.SELECT id, name FROM backup_before WHERE name‘; DROP TABLE injection; --’; B. SELECT id, name FROM user WHERE id23 oR id32 OR 11; C. SELECT id, name FROM user WHERE user.id (SEL…

Vue ElementUI原生upload修改字体大小和区域宽度

Vue ElementUI原生upload修改字体大小和区域宽度 修改后 代码 新增的修改样式代码 .upload-demo /deep/ .el-upload-dragger{width: 700px;height: 300px; }原有拖拽组件代码 <!-- 拖拽上传组件 --><el-uploadclass"upload-demo"dragaction"":m…

React和Vue在前端开发中, 通常选择哪一个

React和Vue的选择需结合具体需求&#xff1a; 选React的场景 大型企业级应用&#xff0c;需处理复杂状态&#xff08;如电商、社交平台&#xff09;团队熟悉JavaScript&#xff0c;已有React技术栈积累需要高度灵活的架构&#xff08;React仅专注视图层&#xff0c;可自由搭配…

Python爬虫实战:研究源码还原技术,实现逆向解密

1. 引言 在网络爬虫技术实际应用中,目标网站常采用各种加密手段保护数据传输和业务逻辑。传统逆向解密方法依赖人工分析和调试,效率低下且易出错。随着 Web 应用复杂度提升,特别是 JavaScript 混淆技术广泛应用,传统方法面临更大挑战。 本文提出基于源码还原的逆向解密方法…

什么是alpaca 或 sharegpt 格式的数据集?

环境&#xff1a; LLaMA-Factory 问题描述&#xff1a; alpaca 或 sharegpt 格式的数据集&#xff1f; 解决方案&#xff1a; “Alpaca”和“ShareGPT”格式的数据集&#xff0c;是近年来在开源大语言模型微调和对话数据构建领域比较流行的两种格式。它们主要用于训练和微调…

OpenCV CUDA模块中矩阵操作------矩阵元素求和

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 在OpenCV的CUDA模块中&#xff0c;矩阵元素求和类函数主要用于计算矩阵元素的总和、绝对值之和以及平方和。这些操作对于图像处理中的特征提取、…

给视频加一个动画。

为什么要给视频加一个动画&#xff1f; 很完整的视频也就是从短动画开始的。遮盖住LOG用。 C:\Users\Sam\Desktop\desktop\startup\workpython\ocr Lottie.py import subprocessdef run_ffmpeg(cmd):print("Running:", " ".join(cmd))subprocess.run(cm…

15:00开始面试,15:06就出来了,问的问题有点变态。。。

从小厂出来&#xff0c;没想到在另一家公司又寄了。 到这家公司开始上班&#xff0c;加班是每天必不可少的&#xff0c;看在钱给的比较多的份上&#xff0c;就不太计较了。没想到4月一纸通知&#xff0c;所有人不准加班&#xff0c;加班费不仅没有了&#xff0c;薪资还要降40%…

使用命令行拉取 Git 仓库

1. 克隆远程仓库&#xff08;首次获取&#xff09; # 克隆仓库到当前目录&#xff08;默认使用 HTTPS 协议&#xff09; git clone https://github.com/用户名/仓库名.git# 克隆仓库到指定目录 git clone https://github.com/用户名/仓库名.git 自定义目录名# 使用 SSH 协议克隆…

如何禁止chrome自动更新

百度了一下 下面这个方法实测有效 目录 1、WINR 输入 services.msc 2、在Services弹窗中找到下面两个service并disable 3、验证是否禁止更新成功&#xff1a; 1、WINR 输入 services.msc 2、在Services弹窗中找到下面两个service并disable GoogleUpdater InternalService…

数据库事务以及JDBC实现事务

一、数据库事务 数据库事务&#xff08;Database Transaction&#xff09;是数据库管理系统中的一个核心概念&#xff0c;它代表一组操作的集合&#xff0c;这些操作要么全部执行成功&#xff0c;要么全部不执行&#xff0c;即操作数据的最小执行单元&#xff0c;保证数据库的…

【vue】【环境配置】项目无法npm run serve,显示node版本过低

解决方案&#xff1a;安装高版本node&#xff0c;并且启用高版本node 步骤&#xff1a; 1、查看当前版本 node -v2、配置nvm下载镜像源 1&#xff09;查看配置文件位置 npm root2&#xff09;找到settings.txt文件 修改镜像源为&#xff1a; node_mirror: https://npmmirro…

WPF之INotifyPropertyChanged实现

文章目录 引言INotifyPropertyChanged接口基础接口定义工作原理 基本实现方式标准实现示例CallerMemberName特性 高级实现技术基类实现通知多个属性变化使用PropertyChanging事件 MVVM框架中的实现MVVM模式简介MVVM框架中的实现Prism框架MVVM Light框架自定义MVVM基类 性能优化…

【MCP教程系列】SpringBoot 搭建基于 Spring AI 的 SSE 模式 MCP 服务

原文地址&#xff1a;https://developer.aliyun.com/article/1662946 在当今快速发展的AI技术背景下&#xff0c;如何高效地集成模型能力成为开发者关注的重点。本文将手把手教你如何基于 Spring AI 搭建支持 SSE&#xff08;Server-Sent Events&#xff09;模式的 MCP 服务 相…

springboot集成langchain4j实现票务助手实战

前言 看此篇的前置知识为langchain4j整合springboot&#xff0c;以及springboot集成langchain4j记忆对话。 Function-Calls介绍 langchain4j 中的 Function Calls&#xff08;函数调用&#xff09;是一种让大语言模型&#xff08;LLM&#xff09;与外部工具&#xff08;如 A…

MySQL-数据库分布式XA事务

准备 innodb存储引擎开启支持分布式事务 set global innodb_support_axonMySQL数据库XA事务的SQL语法如下&#xff1a; XA {START| BEGIN} xid {JOIN | RESUME} XA END xid {SUSPEND [ FOR MIGRATE]} XA PREPARE xid XA COMMIT xid [ONE PHASE] XA ROLLBACK xid XA RECOVER 完…

SAP 运维-冷门问题解决办法

1.SAP Fiori帮助菜单链接如何配置&#xff1f; 答&#xff1a; 执行事务代码HELP_CONFIG&#xff0c;选择对应的Fiori部署模式&#xff0c;配置帮助菜单下的URL链接。 检查配置的帮助菜单&#xff0c;执行事务代码/N//UI2/FLP_CUS_CONF 或者SR13进行查看配置状态与修改。

新型智慧园区技术架构深度解析:数字孪生与零碳科技的融合实践

&#x1f3ed;在杭州亚运村零碳园区&#xff0c;光伏板与氢燃料大巴构成的能源网络&#xff0c;正通过数字孪生技术实现智能调度。这不仅是格力电器与龙源电力在新能源领域的创新实践&#xff0c;更是智慧园区4.0时代的标杆案例。当AI算法开始接管能源调度&#xff0c;当BIM建模…

Java转Go日记(三十六):简单的分布式

1.1.1. 简单的分布式server 目前分布式系统已经很流行了&#xff0c;一些开源框架也被广泛应用&#xff0c;如dubbo、Motan等。对于一个分布式服务&#xff0c;最基本的一项功能就是服务的注册和发现&#xff0c;而利用zk的EPHEMERAL节点则可以很方便的实现该功能。EPHEMERAL节…

机器学习笔记——特征工程

大家好&#xff0c;这里是好评笔记&#xff0c;公主号&#xff1a;Goodnote&#xff0c;专栏文章私信限时Free。本笔记介绍机器学习中常见的特征工程方法、正则化方法和简要介绍强化学习。 文章目录 特征工程&#xff08;Fzeature Engineering&#xff09;1. 特征提取&#xff…