Qt源码分析(一)

  • Post author:
  • Post category:其他



欢迎加QQ群309798848交流C/C++/linux/Qt/音视频/OpenCV


源码面前,了无秘密。阅读源码能帮助我们理解实现原理,然后更灵活的运用。

接下来我用VS2015调试Qt5.9源码。

首先提一下,Qt在WinMain中调用用户的main函数:

我们知道,Qt的类以QObject为首,QWidget,QLabel之类的都是其子类(直接或间接继承)。

那么先来看看QObject的源码,我在下图打了个断点:

void debugQtSource()
{
    QObject* obj = new QObject;
    
}

F11进入,

QObject::QObject(QObject *parent)
    : d_ptr(new QObjectPrivate)
{
    Q_D(QObject);
    d_ptr->q_ptr = this;
    d->threadData = (parent && !parent->thread()) ? parent->d_func()->threadData : QThreadData::current();
    d->threadData->ref();
    if (parent) {
        QT_TRY {
            if (!check_parent_thread(parent, parent ? parent->d_func()->threadData : 0, d->threadData))
                parent = 0;
            setParent(parent);
        } QT_CATCH(...) {
            d->threadData->deref();
            QT_RETHROW;
        }
    }
#if QT_VERSION < 0x60000
    qt_addObject(this);
#endif
    if (Q_UNLIKELY(qtHookData[QHooks::AddQObject]))
        reinterpret_cast<QHooks::AddQObjectCallback>(qtHookData[QHooks::AddQObject])(this);
}

可以看到关键是d_ptr(d_pointer)

d_ptr声明:QScopedPointer<QObjectData> d_ptr;

QScopedPointer的思想是RAII,作用是用户不需要手动delete。

QObjectPrivate继承QObjectData

QObjectData定义:

class Q_CORE_EXPORT QObjectData {
public:
    virtual ~QObjectData() = 0;
    QObject *q_ptr;
    QObject *parent;
    QObjectList children;

    uint isWidget : 1;
    uint blockSig : 1;
    uint wasDeleted : 1;
    uint isDeletingChildren : 1;
    uint sendChildEvents : 1;
    uint receiveChildEvents : 1;
    uint isWindow : 1; //for QWindow
    uint deleteLaterCalled : 1;
    uint unused : 24;
    int postedEvents;
    QDynamicMetaObjectData *metaObject;
    QMetaObject *dynamicMetaObject() const;
};

QObjectData有一个指针q_ptr,指向QObject。

通过d_ptr(new QObjectPrivate);和d_ptr->q_ptr = this; 可以知道QObject和QObjectData都有一个指针互相指向对方。

接下来先看一个关系图:

Qt实现采用了d-pointer这种方式。

使用d-pointer的目的:

实现二进制兼容,头文件与实现细节无关。Qt started out closed source?

二进制兼容:即Qt(dll)版本的更新替换不会影响应用程序exe。因为Qt的接口类(QObject等)的实例的大小是固定的。

在Qt中,QObject,QWidget,QLabel这些都是接口类,真正的实现在QObjectPrivate,QWidgetPrivate,QLabelPrivate 等Private类中。

QObjec的子类,例如QWidget,将QWidgetPrivate*传给QObject的d_ptr。同理QObjectData的子类,例如QWidgetPrivate将QWidget*传给QObjectData的q_ptr。也就是是说着两个类继承树中,各自维护一个指向对方的指针。

但是有一个问题,d_ptr是QObjectData*,我需要将他转为QWidgetPrivate*才能调用QWidgetPrivate的函数或数据。每次使用的时候都要转换指针很麻烦。考虑到代码复用,Qt采用了宏Q_D和Q_Q。

template <typename T> static inline T *qGetPtrHelper(T *ptr) { return ptr; }
template <typename Wrapper> static inline typename Wrapper::pointer qGetPtrHelper(const Wrapper &p) { return p.data(); }

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

#define Q_DECLARE_PRIVATE_D(Dptr, Class) \
    inline Class##Private* d_func() { return reinterpret_cast<Class##Private *>(qGetPtrHelper(Dptr)); } \
    inline const Class##Private* d_func() const { return reinterpret_cast<const Class##Private *>(qGetPtrHelper(Dptr)); } \
    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;

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

如上,Q_D将d_ptr从QObjectData*转换为QWidgetPrivate*,然后赋值给d

Q_Q将q_ptr从QObject*转换为QWidget*,然后赋值给q


关于d-pointer



版权声明:本文为ET_Endeavoring原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。