0.QObject的数据存储

一、QObject概述

QObject类是QT的核心,大部分QT类都是从这个类直接或者间接继承而来。包含QT的信号槽机制和事件机制

QObject通过对象树将对象组织起来,当使用QObject创建一个对象作为其他对象的父对象时,QObject对象会将其他对象自动添加到子对象的列表中,注意,这里的对象数和父子对象关系不是C++里面的父类和子类的关系,只是一种单纯的树型组织结构

 

二、QObject的数据存储

QObject 类的相关文件一共有四个:

qobject.h,QObject class定义的头文件

qobject.cpp,QObject class 和QMetaObject class的实现文件,实现了signal、slot、properties等核心部分

qobjectdefs.h,定义了 QMetaObject class。

qobject_p.h,定义了 QObjectPrivate 类,用来存储 QOjbect 对象的成员数据。

 

先看下qobject.h

class Q_CORE_EXPORT QObject
{
    Q_DECLARE_PRIVATE(QObject)
public:
    Q_INVOKABLE explicit QObject(QObject *parent=nullptr);
    virtual ~QObject();
    //......
protected:
    QObject(QObjectPrivate &dd, QObject *parent = nullptr);
    QScopedPointer<QObjectData> d_ptr;
};

class Q_CORE_EXPORT QObjectData {
    Q_DISABLE_COPY(QObjectData)
public:
    QObjectData() = default;
    virtual ~QObjectData() = 0;
    //......
};

Q_DISABLE_COPY在qglobal.h中,实现如下

#define Q_DISABLE_COPY(Class) \
    Class(const Class &) = delete;\
    Class &operator=(const Class &) = delete;

主要是为了防止QObjectData被拷贝和赋值

qobject.h中,定义了QObject和QObjectData两个类,QObjectData用来存储QObject的数据,并且在QObject被定义为智能指针d_ptr,QScopedPointer保证当指向对象离开作用域后,指向的对象将被删除。防止内存泄露

Q_DECLARE_PRIVATE在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;

Class##Private中的##的作用是连接,当传入QObject后,Class##Private就变成QObjectPrivate

所以,Q_DECLARE_PRIVATE(QObject)展开后代码如下:

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

其中的qGetPtrHelper是个模板函数

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

所以,如果class是QObject,d_func的作用就是将QObject对象的指针转化为QObjectPrivate的指针

 

而QObjectPrivate在qobject_p.h中定义

class Q_CORE_EXPORT QObjectPrivate : public QObjectData
{
    Q_DECLARE_PUBLIC(QObject)
    QObjectPrivate(int version = QObjectPrivateVersion);
    virtual ~QObjectPrivate();
    //下面是signal 和slot 的数据,属性数据等
};

QObjectPrivate继承了QObjectData,由于QObjectData是个纯虚类,所以具体的实现在QObjectPrivate中

而在QObject的构造函数中,将QObjectData的指针d_ptr初始化为QObjectPrivate

QObject::QObject(QObject *parent)
    : d_ptr(new QObjectPrivate)
{
   //......
}

QObject::QObject(QObjectPrivate &dd, QObject *parent)
    : d_ptr(&dd)
{
    //......
}

以具体的QWidget为例,QWidget的定义如下,在qwidget.h的头文件中

class Q_WIDGETS_EXPORT QWidget : public QObject, public QPaintDevice
{
    Q_OBJECT
    Q_DECLARE_PRIVATE(QWidget)

    explicit QWidget(QWidget* parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags());//private构造,防止对QWidget进行拷贝和赋值
    ~QWidget();
    //......
};

QWidget公有继承了QObject

QWidget构造函数的具体实现如下:

QWidget::QWidget(QWidget *parent, Qt::WindowFlags f)
    : QObject(*new QWidgetPrivate, 0), QPaintDevice()
{
    QT_TRY {
        d_func()->init(parent, f);
    } QT_CATCH(...) {
        QWidgetExceptionCleaner::cleanup(this, d_func());
        QT_RETHROW;
    }
}

/*! \internal*/
QWidget::QWidget(QWidgetPrivate &dd, QWidget* parent, Qt::WindowFlags f)
    : QObject(dd, 0), QPaintDevice()
{
    Q_D(QWidget);
    QT_TRY {
        d->init(parent, f);
    } QT_CATCH(...) {
        QWidgetExceptionCleaner::cleanup(this, d_func());
        QT_RETHROW;
    }
}

这两个构造函数都调用了基类QObject的对应的构造函数,并以QWidgetPrivate的对象和指针作为第一个参数传进去。然后调用init函数,d_func()返回的是d_ptr,是QObjectPrivate的指针,而QObjectPrivate是QWidgetPrivate的基类,代码如下

class Q_WIDGETS_EXPORT QWidgetPrivate : public QObjectPrivate
{
    Q_DECLARE_PUBLIC(QWidget)
public:
    explicit QWidgetPrivate(int version = QObjectPrivateVersion);
    ~QWidgetPrivate();
};

所以,基类(QObject)中的d_ptr指针将会指向一个QWidgetPrivate类型的对象,dd将和QWidgetPrivate对象绑定,从而最终调用QWidgetPrivate中的init函数。

所以,通过上述的分析可知,在QT中,QObject类和数据分别是通过两个类QObject和QObjectData来封装的,然后QObjectPrivate又继承了QObjectData,实现了QObjectData中的一部分功能

当要创建一个具体的QT对象时,比如QWidget,会先调用父类QObject构造函数,然后通过C++的多态机制将QWidget的数据部分QWidgetPrivate传入父类QObject的构造函数,从而使对象父类QObject中的数据指针:d_ptr(QObjectPrivate类型的)指向对象的数据QWidgetPrivate,然后就可以使用或操作具体对象的数据了

将类和类的数据分别封装在两个类中,使得类本身和类数据的封装性更好,降低了类数据和类本身之间的耦合

 

参考

Inside Qt Series

 

欢迎大家评论交流,作者水平有限,如有错误,欢迎指出

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值