Qt源码分析(一)

欢迎加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

  • 14
    点赞
  • 46
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Qt 的事件循环是一个非常重要的机制,它负责接收和分发事件,保证 Qt 应用程序的正常运行。下面是简单的 Qt 事件循环源码分析: 1. Qt 的事件循环是通过 `QCoreApplication::exec()` 方法启动的。该方法首先会创建一个 `QEventLoop` 对象,然后进入一个无限循环。 2. 在事件循环中,`QEventLoop` 对象通过调用 `QCoreApplication::processEvents()` 方法来处理当前队列中的事件。该方法会检查是否有待处理的事件,如果没有,则线程会进入休眠状态,等待新的事件到来。 3. 当一个事件到来时,Qt 会根据事件的类型和目标对象,将事件分发给正确的接收者进行处理。接收者可以是窗口部件、控件、布局等。 4. 对于每个事件,Qt 会调用接收者的对应方法来处理。例如,对于鼠标点击事件,Qt 会调用接收者的 `mousePressEvent()` 方法来处理。 5. 在事件处理过程中,如果需要进行其他操作(如更新界面、执行定时器等),Qt 会将这些操作添加到事件队列中。 6. 当所有待处理的事件都被处理完毕后,Qt 会通过调用 `QCoreApplication::quit()` 方法退出事件循环,程序结束运行。 需要注意的是,Qt 的事件循环并不是单线程的。在多线程环境下,每个线程都可以有自己的事件循环,但每个线程只能有一个事件循环。当一个事件需要跨线程传递时,Qt 会通过事件队列和线程间的信号槽机制来实现。 以上是简单的 Qt 事件循环源码分析,如果您对具体的源码细节有更深入的需求,建议参考 Qt 的官方文档和源代码。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值