目录
一、什么是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