Qt : d指针和q指针?

2023-11-10

目录

 

一、什么是d指针和q指针

 1、d指针

2、q 指针 

二、d指针和q指针的作用

三、d指针和q指针的使用 demo


一、什么是d指针和q指针

在Qt的源码中,我们看到大量的Q_D() 和Q_P() 宏的调用,查看代码时在一定程度上增加了复杂度。先看看两个宏的原型:

#define Q_D(Class) Class##Private * const d = d_func() 
#define Q_Q(Class) Class * const q = q_func()          

通过调用Q_D 和Q_Q 宏,我们就得到了大名鼎鼎的d指针和q指针。

 

 1、d指针

#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;  

d_ptr指向私有实现类 ,因此在父类中,我们需要声明一个 私有类型的常指针成员 d_ptr,如:

class MyFatherPrivate;
class MyFather : public QObject
{
    Q_OBJECT
public:
    explicit MyFather(QObject *parent = nullptr);
private:
    MyFatherPrivate * const d_ptr;
    Q_DISABLE_COPY_MOVE(MyFather)
    Q_DECLARE_PRIVATE(MyFather)
};

MyFather::MyFather(QObject *parent): QObject(parent),d_ptr(new MyFatherPrivate(this))
{
}

// ...

2、q 指针 

#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_ptr 指向父类,因此在私有类中,我们会声明一个 父类类型的常指针成员 q_ptr,并在其构造函数的参数列表中进行定义,如:


class MyFatherPrivate:public QObject{
    Q_OBJECT

public:
    MyFatherPrivate(MyFather *parent):q_ptr(parent){ }
public:
    MyFather * const q_ptr;
    Q_DECLARE_PUBLIC(MyFather)
};

 

二、d指针和q指针的作用

d指针和q指针有啥用?肯定不是吃饱了撑着了。

比较多的解释说,这样处理是主要是为了保证代码的二进制兼容性

什么是二进制兼容性?一个库是二进制兼容的,如果一个程序和某个库的某个版本动态链接,并且不需要重新编译,即可在安装有该库较新版本的环境中运行。为什么要保证二进制兼容性?如果不能保证库的二进制兼容性,就意味着每次发布新版本时,依赖该库的所有程序都必须重新编译才能正常运行。显然,这对于像Qt这样被广泛采用的库而言是完全不可接受的。

具体对二进制兼容的详细介绍,可以查看文档《Policies/Binary Compatibility Issues With C++》

其次,还可以  隐藏实现细节 和 提高编译速度(编译优化策略:未发生变化的文件,在编译时可不做修改,快速跳过);

 

三、d指针和q指针的使用 demo

正好今天是父亲节,拿一个Father的类跟大家一起探讨下。

// myfather.h

#ifndef MYFATHER_H
#define MYFATHER_H

#include <QObject>
#include <QString>
#include <QDebug>

class MyFatherPrivate;
class MyFather : public QObject
{
    Q_OBJECT
    Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
    Q_PROPERTY(int age READ age WRITE setAge NOTIFY ageChanged)

public:
    explicit MyFather(QObject *parent = nullptr);

    QString name();
    void setName(QString name);

    int age();
    void setAge(int age);

    void do_smoke();
    bool canSmoke(){ return smoke_enable;}
    void setSmokeEnable(bool enable){ smoke_enable = enable;}

    void do_drink();
    bool canDrink(){ return drink_enable;}
    void setDrinkEnable(bool enable){ drink_enable = enable;}

Q_SIGNALS:
    void nameChanged(QString name);
    void ageChanged(int age);
private:
    MyFatherPrivate * const d_ptr;
    Q_DISABLE_COPY_MOVE(MyFather)
    Q_DECLARE_PRIVATE(MyFather)

    Q_PRIVATE_SLOT(d_func(), void _q_nameChanged(QString name)) // 把槽函数实现在MyClassPrivate 类中,用做MyClass内部使用的槽函数
    Q_PRIVATE_SLOT(d_func(), void _q_ageChanged(int age))

    bool smoke_enable : 1;
    bool drink_enable : 1;
};


class MyFatherPrivate:public QObject{
    Q_OBJECT

public:
    MyFatherPrivate(MyFather *parent):q_ptr(parent){ }

public Q_SLOTS:
    void _q_nameChanged(QString name){
        Q_Q(MyFather);
        _name = name;
        qDebug() <<  __FUNCTION__ << "  can drink =" << q->canDrink() ;
    }

    void _q_ageChanged(int age){
        Q_Q(MyFather);
        _age = age;
        qDebug() << __FUNCTION__  << "  can smoke:" << q->canSmoke() ;
    }
public:
    MyFather * const q_ptr;
    Q_DECLARE_PUBLIC(MyFather)

    QString _name;
    uint _age;
};



#endif // MYFATHER_H

 

// myfather.cpp


#include "myfather.h"

MyFather::MyFather(QObject *parent): QObject(parent),d_ptr(new MyFatherPrivate(this))
{
    connect(this,SIGNAL(nameChanged(QString)),SLOT(_q_nameChanged(QString)));
    connect(this,SIGNAL(ageChanged(int)),SLOT(_q_ageChanged(int)));
}



QString MyFather::name(){
    Q_D(MyFather);
    return d->_name;
}

void MyFather::setName(QString name){
    emit nameChanged(name);
}

int MyFather::age(){
    Q_D(MyFather);
    return d->_age;
}
void MyFather::setAge(int age){
    emit ageChanged(age);
}

void MyFather::do_smoke(){
    if(false == smoke_enable) return;
    qDebug() << "father can smoke.";
}

void MyFather::do_drink(){
    if(false == drink_enable) return;
    qDebug() << "father can drink.";

}

测试:

// main.cpp


#include <QCoreApplication>
#include <myfather.h>
#include <QDebug>

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    MyFather father;
    father.setObjectName("My dear father");
    father.setDrinkEnable(true);
    father.do_drink();
    father.do_smoke();

    father.setProperty("name","Lao sun");
    qDebug() << father.name();

    father.setAge(56);
    qDebug() << father.age();

    return a.exec();
}

输出如下:

father can drink.
_q_nameChanged   can drink = true
"Lao sun"
_q_ageChanged   can smoke: false
56

 

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

Qt : d指针和q指针? 的相关文章

  • Qt-Qlist 检查包含自定义类

    有没有办法覆盖加载自定义类的 Qt QList 的比较机制 即在 java 中你只需要重写一个比较方法 我有一个带有我的自定义类模型的 QList QList
  • 获取按下的按钮的返回值

    我有一个在特定事件中弹出的表单 它从数组中提取按钮并将标签值设置为特定值 因此 如果您要按下或单击此按钮 该函数应返回标签值 我怎样才能做到这一点 我如何知道点击了哪个按钮 此时代码返回 DialogResult 但我想从函数返回 Tag
  • pthread_cond_timedwait() 和 pthread_cond_broadcast() 解释

    因此 我在堆栈溢出和其他资源上进行了大量搜索 但我无法理解有关上述函数的一些内容 具体来说 1 当pthread cond timedwait 因为定时器值用完而返回时 它如何自动重新获取互斥锁 互斥锁可能被锁定在其他地方 例如 在生产者
  • QML 连接:不推荐使用连接中隐式定义的 onFoo 属性

    升级到 Qt 5 15 时收到以下错误消息 QML Connections Implicitly defined onFoo properties in Connections are deprecated Use this syntax
  • 将布尔参数传递给 SQL Server 存储过程

    我早些时候问过这个问题 我以为我找到了问题所在 但我没有 我在将布尔参数传递给存储过程时遇到问题 这是我的 C 代码 public bool upload false protected void showDate object sende
  • 指针问题(仅在发布版本中)

    不确定如何描述这一点 但我在这里 由于某种原因 当尝试创建我的游戏的发布版本进行测试时 它的敌人创建方面不起作用 Enemies e level1 3 e level1 0 Enemies sdlLib 500 2 3 128 250 32
  • C 预处理器库

    我的任务是开发源分析工具C程序 并且我需要在分析本身之前预处理代码 我想知道什么是最好的图书馆 我需要一些重量轻 便于携带的东西 与其推出自己的 为什么不使用cpp这是的一部分gcc suite http gcc gnu org onlin
  • 使用 System.Text.Json 即时格式化 JSON 流

    我有一个未缩进的 Json 字符串 例如 hash 123 id 456 我想缩进字符串并将其序列化为 JSON 文件 天真地 我可以使用缩进字符串Newtonsoft如下 using Newtonsoft Json Linq JToken
  • C# 中的递归自定义配置

    我正在尝试创建一个遵循以下递归结构的自定义配置部分
  • for循环中计数器变量的范围是多少?

    我在 Visual Studio 2008 中收到以下错误 Error 1 A local variable named i cannot be declared in this scope because it would give a
  • Discord.net 无法在 Linux 上运行

    我正在尝试让在 Linux VPS 上运行的 Discord net 中编码的不和谐机器人 我通过单声道运行 但我不断收到此错误 Unhandled Exception System Exception Connection lost at
  • 实体框架 4 DB 优先依赖注入?

    我更喜欢创建自己的数据库 设置索引 唯一约束等 使用 edmx 实体框架设计器 从数据库生成域模型是轻而易举的事 现在我有兴趣使用依赖注入来设置一些存储库 我查看了 StackOverflow 上的一些文章和帖子 似乎重点关注代码优先方法
  • 插入记录后如何从SQL Server获取Identity值

    我在数据库中添加一条记录identity价值 我想在插入后获取身份值 我不想通过存储过程来做到这一点 这是我的代码 SQLString INSERT INTO myTable SQLString Cal1 Cal2 Cal3 Cal4 SQ
  • C++ fmt 库,仅使用格式说明符格式化单个参数

    使用 C fmt 库 并给定一个裸格式说明符 有没有办法使用它来格式化单个参数 example std string str magic format 2f 1 23 current method template
  • 控制到达非 void 函数末尾 -wreturn-type

    这是查找四个数字中的最大值的代码 include
  • 如何让Gtk+窗口背景透明?

    我想让 Gtk 窗口的背景透明 以便只有窗口中的小部件可见 我找到了一些教程 http mikehearn wordpress com 2006 03 26 gtk windows with alpha channels https web
  • 为什么 C# Math.Ceiling 向下舍入?

    我今天过得很艰难 但有些事情不太对劲 在我的 C 代码中 我有这样的内容 Math Ceiling decimal this TotalRecordCount this PageSize Where int TotalRecordCount
  • Process.Start 阻塞

    我正在调用 Process Start 但它会阻止当前线程 pInfo new ProcessStartInfo C Windows notepad exe Start process mProcess new Process mProce
  • const、span 和迭代器的问题

    我尝试编写一个按索引迭代容器的迭代器 AIt and a const It两者都允许更改容器的内容 AConst it and a const Const it两者都禁止更改容器的内容 之后 我尝试写一个span
  • 如何在 C++ BOOST 中像图形一样加载 TIFF 图像

    我想要加载一个 tiff 图像 带有带有浮点值的像素的 GEOTIFF 例如 boost C 中的图形 我是 C 的新手 我的目标是使用从源 A 到目标 B 的双向 Dijkstra 来获得更高的性能 Boost GIL load tiif

随机推荐

  • [论文阅读] (04) 人工智能真的安全吗?浙大团队外滩大会分享AI对抗样本技术

    外滩大会 AI安全 智能时代的攻守道 Deep Learning Security From the NLP Perspective 浙江大学 秀璋带你读论文 系列主要是督促自己阅读优秀论文及听取学术讲座 并分享给大家 希望您喜欢 由于作者
  • Labelme安装及使用教程

    Labelme安装教程 基于anaconda 1 创建anaconda虚拟环境labelme conda create n labelme python 3 6 完成之后如图所示 由于我已经创建了labelme故这里用labelme1代替
  • 产品思维用户思维

    用户思维是一种关注用户需求 体验和价值的思维方式 将用户放在产品设计 开发和提供服务的核心位置 它强调了理解用户在不同场景下的需求 提供与之相匹配的解决方案 从而帮助用户实现他们的目标 描述一个用户时 可以从不同角度来考虑 按人口属性描述用
  • 什么是搜索引擎?

    搜索引擎 搜索引擎是指根据一定的策略 运用特定的计算机程序从互联网上搜集信息 在对信息进行组织和处理后 为用户提供检索服务 将用户检索相关的信息展示给用户的系统 搜索引擎包括全文索引 目录索引 元搜索引擎 垂直搜索引擎 集合式搜索引擎 门户
  • 排序算法-选择排序

    属性 基本思想 每一次从待排序的数据元素中选出最小 或最大 的一个元素 存放在序列的起始位置 直到全部待排序的数据元素排完 过程 在元素集合array i array n 1 中选择关键码最大 小 的数据元素 若它不是这组元素中的最后一个
  • 刷脸支付完全融入了我们的日常生活

    现金支付的假币 丢失等问题层出不穷 随着现金交易出现的不便 银行卡的出现成为人们支付方式的一大转变 智能手机的发展和网络科技的进步催生了网络支付方式 AI智能技术的不断发展又让人们迎来了一场刷脸支付的新革命 刷脸支付是指用户在购物后的支付认
  • rabbitmq 连接报错 An unexpected connection driver error occured(亲测)

    在服务器上安装了一个RabbitMq 并新创建了一个用户授予了管理员角色 登录控制台查看一切正常 兴高采烈启动项目进行连接 结果一盆冷水下来 报如下错误 o s a r l SimpleMessageListenerContainer Fa
  • git中format-patch和chery-pick的区别和联系

    chery pick 把其他分支的一次或多次commit 在当前分支上重演 典型的使用场景 其他分支有很多提交 但是你只对其中的一部分感兴趣 这时候可以使用chery pick 只挑选其他分支感兴趣的commit 合并到自己的分支中 for
  • mybatis if-else(写法)

  • Debian GNU/Linux 中以源码方式安装Odoo 14(社区版)

    Odoo是一种流行的开源商务应用程序套件 可帮助公司管理和运营其业务 也可用于在线教学 它包括广泛的应用程序 Debian GNU Linux 是社区版服务器的代表 本文将介绍如何在Debian GNU Linux中以源码方式安装和部署Od
  • 垃圾回收之CMS GC

    一 六个阶段 阶段 1 Initial Mark 初始标记 这个阶段伴随着 STW 暂停 初始标记的目标是标记所有的 根对象 包括根对象直接引用的对象 以及被年轻代中所 有存活对象所引用的对象 老年代单独回收 阶段 2 Concurrent
  • 剑指 Offer 57. 和为s的两个数字(java+python)

    输入一个递增排序的数组和一个数字s 在数组中查找两个数 使得它们的和正好是s 如果有多对数字的和等于s 则输出任意一对即可 示例 1 输入 nums 2 7 11 15 target 9 输出 2 7 或者 7 2 示例 2 输入 nums
  • MyBatis快速入门

    Mybatis概述 Mybatis概念 MyBatis 是一款优秀的持久层框架 用于简化 JDBC 开发 MyBatis 本是 Apache 的一个开源项目iBatis 2010年这个项目由apache software foundatio
  • show process cpu

    Router show proc cpu CPU utilization for five seconds 63 50 one minute 58 five minutes 58 PID Runtime ms Invoked uSecs 5
  • NVM在windows下切换node版本

    如果您很忙或者很急 请直接阅读 三 步骤 一 问题背景 生活里偶尔穿梭在大街小巷中 工作中时常并行于多项目任务里 当多个项目并行时 由于创建的时间或人为选择等因素 各个项目里有着差异的node版本 这样我们在不同的项目里需要切换不同版本的N
  • Linux软链接与硬链接区别

    一 背景 链接 是一种在共享文件和访问它的用户的若干目录项之间建立联系的一种方法 Linux中包括两种链接 硬链接 Hard Link 和软链接 Soft Link 软链接又称为符号链接 Symbolic link 要了解链接 我们首先得了
  • vscode 问题解决:“检测到 #include 错误,请更新 includePath”

    当我们在使用vscode进行编辑代码时 往往会遇到以下警告 这表明 在我们的代码中 无法找到对应的头文件 但问题在于 阅读和编辑代码 需要保证代码的统一性 所以我们需要知道这个头文件到底在哪 我们也需要让vscode通过点击该头文件名就可以
  • EI、Scopus双检索

    会议简介 Brief Introduction 2023年第四届自动化 机械与设计工程国际会议 SAMDE 2023 会议时间 2023年12月8 10日 召开地点 中国 南京 大会官网 www samde org 机械设计制造及其自动化学
  • python第三方库集锦

    环境管理管理 Python 版本和环境的工具 p 非常简单的交互式 python 版本管理工具 pyenv 简单的 Python 版本管理工具 Vex 可以在虚拟环境中执行命令 virtualenv 创建独立 Python 环境的工具 vi
  • Qt : d指针和q指针?

    目录 一 什么是d指针和q指针 1 d指针 2 q 指针 二 d指针和q指针的作用 三 d指针和q指针的使用 demo 一 什么是d指针和q指针 在Qt的源码中 我们看到大量的Q D 和Q P 宏的调用 查看代码时在一定程度上增加了复杂度