数据隐藏之Qt中d指针详解

2023-11-14

最近看到代码有用到了Qt中的Q_D指针,就去学习了下,发现真的很好用,因此写一篇文章总结下。

//student.h
class CStudent
{
public:
	CStudent();
	~CStudent();
private:
	string m_name;
	int    m_age;
	int    m_grade;
};
如果我们这个类属于一个动态库,那么向外部提供一个dll时,就会暴露我类的数据结构,使用者便会知道,原来姓名是string类型,年龄是int类型等等,这是我不想看到的,也不符合我把其设置为private的初衷。
那怎么才能解决上面的问题呢?也许我们可以这么做:

//student.h
class CStudentDate;
class CStudent
{
public:
	CStudent();
	~CStudent();
private:
	CStudentDate* d_ptr;
};

//student.cpp
class CStudentDate
{
public:
	string m_name;
	int    m_age;
	int    m_grade;
};

      我们将CStudent的数据全部放到他的数据管理类中,将其数据管理类全部放到其cpp中实现,这样即使你像人家提供你的dll和.h文件也不会暴露你的成员的数据类型。这就是一种数据隐藏方式。其优点是隐藏了我们不想透露的数据信息,而且我们即使添加数据项如在添加一个班级成员等也不会影响CStudent类的大小。能做到二进制兼容。

       好了,我们回归到前面的d指针上来,其他d指针就是Qt为了实现信息隐藏的一种方式。其具体实现原理和上面的好了,我们回归到前面的d指针上来,其他d指针就是Qt为了实现信息隐藏的一种方式。其具体实现原理和上面的CStudent一样,相当于我们的d_ptr指针。一样,相当于我们CStudent类中的d_ptr指针。下面一起来看看Qt的 d指针是怎么实现的

以下内容来自:http://blog.csdn.net/rabinsong/article/details/9474859



如上图,这个是Qt根结点的指针的一般形式,下面来看看非根结点的一般形式,

注意这里QWidge派生自QObject,它里面没有d_ptr,但是它的成员函数可以访问d_ptr,因为 d_ptr是保护成员,且它的对象模型包含 d_ptr(这是因为派生类继承父类的所有成员)。


下面我们来看看Qt对上述两种情况是怎么实现的:

qobject.h文件:
  
QObjectData {  
public:  
    QObject *q_ptr;  
    ...  
};  
  
class Q_CORE_EXPORT QObject  
{  
    ...  
    Q_DECLARE_PRIVATE(QObject)  
public:  
    Q_INVOKABLE explicit QObject(QObject *parent=0);  
    virtual ~QObject();  
    ...  
  
protected:  
    QObject(QObjectPrivate &dd, QObject *parent = 0);  
    ...      
      
protected:  
    QScopedPointer<QObjectData> d_ptr;  
    ...  
};  
如上,在这里我算去了其他的项,只保留了于d_ptr有关的项,首先来看看Q_DECLARE_PRIVATE(QObject)是什么:
    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;  
根据宏定义,则Q_DECLARE_PRIVATE(QObject)翻译如下:
inline QObjectPrivate *d_func()  
{  
    return reinterpret_cast<QObjectPrivate *>(qGetPtrHelper(d_ptr));   
}  
inline const QObjectPrivate *d_func() const  
{   
    return reinterpret_cast<const QObjectPrivate *>(qGetPtrHelper(d_ptr));  
}  
friend class QObjectPrivate;

再来看看qGetPtrHelper的定义:

template <typename T> static inline T *qGetPtrHelper(T *ptr)  
{   
    return ptr;  
}  
再来看QScopePointer,它类似于智能指针,这样不用关心 d_ptr的释放,当离开QScopePointer的作用范围,QScopePointer会自动释放d_ptr指向的堆内存,那麽这个指针是什么时候生成的呢?q_ptr又是什么时候赋值的呢?让我们来看看qobject.cpp的实现:
QObject::QObject(QObject *parent)  
    : d_ptr(new QObjectPrivate)  
{  
    Q_D(QObject);  
    d_ptr->q_ptr = this;  
    ...  
}  
  
QObject::QObject(QObjectPrivate &dd, QObject *parent)  
    : d_ptr(&dd)  
{  
    Q_D(QObject);  
    d_ptr->q_ptr = this;  
    ...  
}  
我们看第一个构造函数,对于根结点的d_ptr指向new QObjectPrivate,而QObjectPrivate派生自QObjectData,那麽Q_D(QObject)宏表示什么意思呢?
#define Q_D(Class) Class##Private * const d = d_func()  
Q_D(QObject);翻译如下:
  
QObjectPrivate * const d = d_func(); 
不难看出Q_D(QObject);定义了一个QObjectPrivate的常量指针,指向d_func() 的返回值,而该返回值,正是d_ptr(见头文件 d_func()的定义),因此同过Q_D宏我们就可以访问d指针了。
对于第二个构造函数稍后介绍,下面来看看非根结点的d_ptr的实现情况:
头文件:

class Q_CORE_EXPORT QObjectPrivate : public QObjectData  
{  
    Q_DECLARE_PUBLIC(QObject)  
    ...  
};  
  
class Q_GUI_EXPORT QWidgetPrivate : public QObjectPrivate  
{  
    Q_DECLARE_PUBLIC(QWidget)  
    ...  
};  
  
class Q_GUI_EXPORT QWidget : public QObject  
{  
    ...  
    Q_DECLARE_PRIVATE(QWidget)  
    ...  
public:  
    ...  
    explicit QWidget(QWidget* parent = 0, Qt::WindowFlags f = 0);  
    ...  
};  
我们首先来看看Q_DECLARE_PUBLIC宏:
#define Q_DECLARE_PUBLIC(Class)                                    \  
    inline Class* q_func() { return static_cast<Class *>(q_ptr); } \  
    inline const Class* q_func() const { return static_cast<const Class *>(q_ptr); } \  
    friend class Class;  
根据宏定义,Q_DECLARE_PUBLIC(QObject)翻译如下:
  
inline QObject *q_func()  
{   
    return static_cast<QObject *>(q_ptr);  
}   
inline const QObject *q_func() const   
{   
    return static_cast<const QObject *>(q_ptr);  
}   
friend class QObject;  
Q_DECLARE_PUBLIC(QWidget)翻译如下:
  
inline QWidget *q_func()  
{   
    return static_cast<QWidget *>(q_ptr);  
}   
inline const QWidget *q_func() const   
{   
    return static_cast<const QWidget *>(q_ptr);  
}   
friend class QWidget;  
注意这里的q_ptr是在QObjectData里公有声明的,QObjectPrivate,QWidgetPrivate都派生或间接派生自QObjectData,所以可以访问q_ptr。
接下来看Q_DECLARE_PRIVATE(QWidget)的翻译:
inline QWidgetPrivate *d_func()  
{  
    return reinterpret_cast<QWidgetPrivate *>(qGetPtrHelper(d_ptr));   
}  
inline const QWidgetPrivate *d_func() const  
{   
    return reinterpret_cast<const QWidgetPrivate *>(qGetPtrHelper(d_ptr));  
}  
friend class QWidgetPrivate;
接下来看看QWidget的构造函数的实现:
QWidget::QWidget(QWidget *parent, Qt::WindowFlags f)  
    : QObject(*new QWidgetPrivate, 0)  
{  
    ...  
} 
看到QObject(*new QwidgetPrivate, 0)这里调用了QObject的第二个构造函数,将d_ptr指向new QWidgetPrivate所指向的堆内存。


本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

数据隐藏之Qt中d指针详解 的相关文章

  • 获取小部件的背景颜色 - 真的

    我无法获取小部件的实际背景颜色 在我的特殊情况下 我在使用 QTabWidget 中的小部件时遇到问题 这是在Windows7上 因此 经典的小部件有一些灰色背景 而选项卡内的小部件通常用白色背景绘制 I tried def bgcolor
  • [本机]:在Qt for Android中使用Java函数和第3方库[关闭]

    Closed 这个问题是基于意见的 help closed questions 目前不接受答案 最近我用qt写了一个android应用程序 但我有一个很大的问题 我可以使用 调用一些原生的android API 比如调用特殊的activit
  • Windows 消息循环而不是 QApplication::exec() / QApplication::processEvents()

    我是否想念任何一个Qt如果我替换功能QApplication exec 使用标准 Windows 消息循环实现 这应该可以澄清我的意思 运行事件处理的常用 Qt 方式 int main int argc char argv QApplica
  • QLineEdit:显示处理后的文本,而不是输入的文本,但保留它(自定义回显模式)

    我想要一个QLineEdit不显示输入的文本 而是显示经过处理的版本 同时保留原始文本并在通过请求时返回它text 就像密码回显模式一样 但我不希望每个字符都被屏蔽 我想虚拟化空间 例如什么时候some text with spaces i
  • QT 中只获取文件而不获取目录?

    当我这样做时 QDir myDir home some location QStringList filesList myDir entryList 它返回该位置内的文件和目录 但我只想要文件 并且这些文件可以具有任意扩展名 有任何想法吗
  • 在 QtCreator 中将 OpenCV 2.3 与 Qt 结合使用

    随着 OpenCV 2 3 版本终于发布 我想在我的系统上编译并安装这个最新版本 由于我经常使用 Qt 和 QtCreator 我当然希望能够在我的 Qt 项目中使用它 我已经尝试了几种方法几个小时 但总是出现错误 第一次尝试 使用WITH
  • 从 QML 实例化 C++ 对象会产生巨大的内存使用开销

    实例化一个QObjectC 堆中的派生类为每个对象提供了大约 160 个字节 通过注册相同的对象qmlRegisterType 用于从 QML 创建并通过以下方式动态创建对象createObject 每个对象给我 2000 多个字节 这是完
  • 仅将非模态 QDialog 窗口放置在我的应用程序顶部,而不是所有应用程序顶部

    我有一个 QDialog 窗口 它应该始终位于我的应用程序顶部 它不是模态的 用户可以随时与对话框和主应用程序进行交互 使用窗口保持在顶部提示在某种程度上实现了这一点 但是 该对话框仍然位于所有其他正在运行的应用程序 例如记事本 chrom
  • QSpinBox 输入 NaN 作为有效值

    我正在尝试扩展 QSpinBox 以能够输入 NaN 或 nan 作为有效值 根据文档 我应该使用 textFromValue valueFromText 和 validate 函数来完成此操作 但我无法让它工作 因为它仍然不允许我输入除数
  • QDesktopServices::openUrl 在资源管理器中选择指定文件

    在大多数编码程序中 您可以右键单击该项目 然后单击 在资源管理器中显示 它会在资源管理器中显示选定项目的文件 在 Qt 中使用 QDesktopServices 如何做到这一点 或在 QT 中执行此操作的任何方式 您可以使用此方法在 Win
  • (如何)我可以抑制未找到包配置文件的警告吗?

    我正在尝试创建一个CMakeLists txt尝试查找的文件Qt5 如果失败 则尝试回退到Qt4安装 该脚本到目前为止有效 但如果出现以下情况我总会收到警告Qt5未安装 注意FindQt5 cmake是由提供Qt5并且仅当以下情况时才可用Q
  • Qt 创建者 + MITK (Linux)

    我正在尝试使用MITK 与 Qt Creator 我已经通过 ccmake 成功编译并使用了 VTK 和 ITK 我已经编译了 MITK超级建造模式 它下载 CTK VTK ITK 等 然后我就配置好了 我已经用 make 编译了 大约两个
  • Qt WinRT 应用程序无法访问文件权限被拒绝

    我需要使用 Qt 和 FFMPEG 开发 WinRT 应用程序 我根据指令构建了 WinRT 的 ffmpeghere https github com Microsoft FFmpegInterop我可以将库与我的项目链接起来 现在我需要
  • 如何在带有预编译头的项目中使用google protobuf

    我有一个包含多个项目的解决方案 我的项目 但不是全部 使用预编译头 我决定使用 protobuf 但遇到了一个问题 在 protoc exe 从 proto 生成 pb h 后 我尝试包含标头并收到错误 预编译标头未包含在 pb h 中 我
  • 加权 Voronoi 的 CGAL 2D APOLLONIUS 图 - 如何生成和获取面和顶点?

    我正在尝试根据阿波罗尼乌斯图生成加权沃罗诺伊 我正在使用 CGAL 库 我找不到如何从 apollonius 获取面和顶点的好例子 我有以下类型定义 typedef double NT typedef CGAL Cartesian lt N
  • QGraphicsScene::clear 不会改变 sceneRect

    我有一个 QGraphicsScene 场景 和 QGraphicsView graphicsView 我有一个画图的方法 当我需要重绘所有图形时 我调用这个方法 一切都好 但我意识到 scene gt clear 不会改变 sceneRe
  • 重定向 python 交互式帮助()

    我正在为使用 Qt 的应用程序开发交互式 python shell 但是我似乎无法获得重定向的交互式帮助 我的 python 代码中有这个 class OutputCatcher def init self self data def wr
  • 如何将图像显示为缩略图

    我有一个QTreeView显示硬盘驱动器和目录 我也有一个QListView显示图像文件如下 但我想将图像显示为缩略图 如下所示 My code mainWidget mainWidget QWidget parent QWidget pa
  • QCombobox 向下箭头图像

    如何更改Qcombobox向下箭头图像 现在我正在使用这个 QSS 代码 但这不起作用 我无法删除向下箭头边框 QComboBox border 0px QComboBox down arrow border 0px background
  • 同时从多个流中捕获、最佳方法以及如何减少 CPU 使用率

    我目前正在编写一个应用程序 该应用程序将捕获大量 RTSP 流 在我的例子中为 12 个 并将其显示在 QT 小部件上 当我超过大约 6 7 个流时 问题就会出现 CPU 使用率激增并且出现明显的卡顿 我认为它不是 QT 绘制函数的原因是因

随机推荐

  • Linux系统调优 之 《OpenStack平台调度策略优化》

    目录 1 规划节点 2 基础准备 案例实施 1 OpenStack平台报错分析 2 解决策略 1 规划节点 根据云平台分配的主机 节点规划见表1 表1 节点规划 IP 主机名 节点 192 168 200 11 controller 控制节
  • delphi ado 动态连接数据库

    unit Unit1 interface uses Windows Messages SysUtils Variants Classes Graphics Controls Forms Dialogs StdCtrls DB ADODB E
  • Contains Duplicate III

    Given an array of integers find out whether there are two distinct indices i and j in the array such that the difference
  • 常用问题网址

    https www leonelngande com fetching the current route fragment in angular 7 RxJS https github com manojjha86 complete an
  • Router-Link详解

  • 文件(或文件夹)的复制(Java)

    将源文件 或目录 复制到另一个目录里 三种方法 1 最普通的方法 主要用File类中的方法和IO流相关的类进行递归复制 2 主要用Files类中的copy 方法递归复制 3 主要用Files类中的copy 方法和walkFileTree 方
  • 构建Buildroot根文件系统(I.MX6ULL)

    Busybox构建的根文件系统只有一些常用的命令和文件 Buildroot不仅集成了 busybox 而且还集成了各种常见的第三方库和软件 开发环境 Buildroot 版本 buildroot 2019 02 6 tar gz 虚拟机 4
  • XSS-通关小游戏(1-20)

    在玩游戏之前先简单的了解下 什么是XSS 1 什么是xss XSS攻击全称跨站脚本攻击 是为不和层叠样式表 Cascading Style Sheets CSS 的缩写混淆 故将跨站脚本攻击缩写为XSS XSS是一种在web应用中的计算机安
  • Unity --- UGUI(Unity Graphical user interface)--- Canvas画布

    1 UI User Interface 使用者与机器之间的交互界面 1 所谓的自适应系统指的是分辨率的适应 比如在一个分辨率下做的UI放到另一个分辨率下显示时 如果没有自适应系统的话就会导致UI过大 过小 被辟成一半等等情况 而有了自适应系
  • Android:项目结构

    前言 默认情况下 在 Android Studio 中创建 Android 项目后 将默认生成 Project Packages Scratches Android Project Fines Problems Production Tes
  • 性能指标有哪些

    1 响应时间 Response time 响应时间就是用户感受软件系统为其服务所耗费的时间 对于网站系统来说 响应时间就是从点击了一个页面计时开始 到这个页面完全在浏览器里展现计时结束的这一段时间间隔 看起来很简单 但其实在这段响应时间内
  • 开源GIS浅谈

    开源GIS浅谈 转 http blog csdn net happyduoduo1 article details 51773850 谈到GIS软件 首先让我们想到的是GIS界的龙头大哥ESRI公司旗下的ArcGIS产品 从最初接触的ver
  • js中的微任务和宏任务,附面试题

    因为javascript是一门单线程语言 所以代码的解析执行都要以自上而下的执行 直到任务队列 task queue 的出现 js开始有了异步任务 当一段代码需要稍后执行时 便可以使用异步方案 setTimeout setInterval
  • Eclipse C debug报错Can‘t find a source file at “xxxxx“Locate the file or edit the source lookup path

    笔记备忘 1 操作入下 Debug configerations进入如下界面 双击你的放置器对应的选项 添加新的选项 在source位置记得添加如下选项 2 解决完上面报错还提示如下 no source for main step1 工程右
  • C++构造函数的各种用法全面解析(C++初学面向对象编程)

    文章目录 一 构造函数的基本用法 二 带参构造函数与其调用 三 拷贝构造函数 四 构造函数的重载 一 构造函数的基本用法 1 构造函数概念 一个类的对象被创建的时候 编译系统对象分配内存空间 并自动调用该构造函数 由构造函数完成成员的初始化
  • 解决:如何将pytorch的版本改为和cuda对应、如何使用笔记本电脑自带的NVIDIA使用GPU跑深度学习

    Step1 安装cuda 网址 https developer nvidia com cuda toolkit archive PS 此处必须先看看电脑显卡是否自己就装了cuda 可以通过执行命令行语句nvcc V以此检查cuda是否有 如
  • 字典表设计

    为什么字典表 存在问题 某些变量在多个地方使用 而且一般是固定的 但是随着系统升级和后期变化 可能需要改变 如果这些变量写死在代码里面将会变得难以维护 所以要将其从代码中抽离出来 一般的业务系统客户端与用户交互的时候都会使用下拉框组件 对于
  • Kafka3.0.0版本——消费者(分区的分配以及再平衡)

    目录 一 分区的分配以及再平衡 1 1 消费者分区及消费者组的概述 1 2 如何确定哪个consumer来消费哪个partition的数据 1 3 消费者分区分配策略 一 分区的分配以及再平衡 1 1 消费者分区及消费者组的概述 一个con
  • 样本的均值和方差的无偏估计与测试阶段均值方差的关系

    什么是无偏估计 估计是用样本统计量 可以理解为随机抽样 来估计总体参数时的一种无偏推断 无偏估计的要求就是 估计出来的参数的数学期望等于被估计参数的真实值 所以呢 可以看出 估计值也是一个变量 因为是随机的嘛 真实值谁也不知道啊 因为你不可
  • 数据隐藏之Qt中d指针详解

    最近看到代码有用到了Qt中的Q D指针 就去学习了下 发现真的很好用 因此写一篇文章总结下 student h class CStudent public CStudent CStudent private string m name in