浅谈Q_D指针和Q_Q指针

9 篇文章 0 订阅

最近开始研究QT源码,鉴于群友说把知识点分享出来,所以决定分享出来,文笔不是很好,勿喷。未来的计划是会每周发一篇关于源码分析的文章,持续到2021年。这也是我2021年的学习计划之一。

进入正题

Q_D指针在QT源码中大量的使用,其根本目的在于解决二进制兼容问题。

何为二进制兼容

1.动态链接到库的前一个版本的程序继续与库的新版本一起运行而不需要重新编译,则库是二进制兼容的。

2.程序需要重新编译以使用新版本的库运行,但不需要任何进一步的修改,则该库是源代码兼容的。

要能够实现二进制兼容,就要求每一个结构以及每一个对象的数据模型保持不变。所谓“数据模型保持不变”,就是不能在类中增加、删除数据成员。这是 C++ 编译器要求的,其根本是保持数据成员在数据模型中的位移保持不变。那么如何实现?很简单,我们让所有的共有类都拥有一个指针,这个指针包含所有数据的私有数据结构。且这个私有的数据结构可在任意版本中修改,从而不影响该类的使用,因为这个指针是私有的,外部调用只能看到一个不变化的共有类,该共有类只拥有一个指针,这个指针就是称为d指针的Q_D指针。

首先看源码内Q_D的声明,源码部分只截取了关键的部分:

qobject.h

class Q_CORE_EXPORT QObjectData {
    Q_DISABLE_COPY(QObjectData)
public:
    QObjectData() = default;
    virtual ~QObjectData() = 0;
    QObject *q_ptr;
    QObject *parent;
    ......
};
class Q_CORE_EXPORT QObject
{
    Q_OBJECT

    Q_PROPERTY(QString objectName READ objectName WRITE setObjectName NOTIFY objectNameChanged)
    Q_DECLARE_PRIVATE(QObject)
public:
    Q_INVOKABLE explicit QObject(QObject *parent=nullptr);
    virtual ~QObject();
    ......
protected:
    QObject(QObjectPrivate &dd, QObject *parent = nullptr);
protected:
    QScopedPointer<QObjectData> d_ptr;
}

public:
    Q_INVOKABLE explicit QObject(QObject *parent=nullptr);
    virtual ~QObject();
紧接着再看看QScopedPointer

QScopedPointer Class​doc.qt.io/qt-5/qscopedpointer.html

查阅文档可知这是QT自己提供的 smart pointer。智能指针的作用相信学过C++的都知道是什么。接着往下看这个是在什么时候进行的赋值

qobject.cpp

QObject::QObject(QObject *parent)
    : d_ptr(new QObjectPrivate)
{
    Q_ASSERT_X(this != parent, Q_FUNC_INFO, "Cannot parent a QObject to itself");

    Q_D(QObject);
    d_ptr->q_ptr = this;
    .......
}
QObject::QObject(QObjectPrivate &dd, QObject *parent)
    : d_ptr(&dd)
{
    Q_ASSERT_X(this != parent, Q_FUNC_INFO, "Cannot parent a QObject to itself");

    Q_D(QObject);
    d_ptr->q_ptr = this;
    .......
}

我们再看看Q_D()这个宏

qglobal.h

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

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

预处理器会把##前后进行强制连接,我们把这个宏展开,就会得到以下函数:

inline QObjectPrivate *d_func()  
{  
    return reinterpret_cast<QObjectPrivate *>(qGetPtrHelper(d_ptr));   
} 
inline const Class##Private* d_func() const 
{
 Q_CAST_IGNORE_ALIGN(return reinterpret_cast<const Class##Private *>(qGetPtrHelper(d_ptr));
} 
    

再看看qGetPtrHelper这个函数的定义

qglobal.h

template <typename T> inline T *qGetPtrHelper(T *ptr) { return ptr; }
template <typename Ptr> inline auto qGetPtrHelper(Ptr &ptr) -> decltype(ptr.operator->()) { return ptr.operator->(); }

从代码得知,定义了一个qobjec.cpp中定义了一个QObject的常量指针,保存了d_fun()的返回值也就得d_ptr。

注意我们再回头看看q_prt的声明

class Q_CORE_EXPORT QObjectData {
    Q_DISABLE_COPY(QObjectData)
public:
    QObjectData() = default;
    virtual ~QObjectData() = 0;
    QObject *q_ptr;
    QObject *parent;
    ......
};

qptr在QObjectData里声明为public。如何访问q_prt也就不言而喻了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值