Qt源码解析之QObject

本文详细解读了Qt中的QObject内存管理机制,包括父对象如何通过QObjectList自动管理子部件,以及线程亲和性如何确保对象间的同步。还介绍了构造函数中的ThreadAffinity设置和事件循环的线程约束。
摘要由CSDN通过智能技术生成

一、半自动的内存管理

QDialog dlg(this);
QPushButton *btn = new QPushButton(&dlg);//dlg析构时,会先delete掉btn
dlg.exec();
 1.首先看一下QObject的构造函数:
//E:\Qt\qt-everywhere-src-6.2.1\qtbase\src\corelib\kernel\qobject.cpp:928
QObject::QObject(QObject *parent)
    : QObject(*new QObjectPrivate, parent)
{
}
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;
    auto threadData = (parent && !parent->thread()) ? parent->d_func()->threadData.loadRelaxed() : QThreadData::current();
    threadData->ref();
    d->threadData.storeRelaxed(threadData);
    if (parent) {
        QT_TRY {
            if (!check_parent_thread(parent, parent ? parent->d_func()->threadData.loadRelaxed() : nullptr, threadData))
                parent = nullptr;
            if (d->isWidget) {
                if (parent) {
                    d->parent = parent;	//记录父指针
                    d->parent->d_func()->children.append(this);	//加入到父对象的children列表
                }
                // no events sent here, this is done at the end of the QWidget constructor
            } else {
                setParent(parent); //设置parent,后面会展开
            }
        } QT_CATCH(...) {
            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);
    Q_TRACE(QObject_ctor, this);
}
 parent可以视为改QObject的所有者,如果在构造QObject对象时传入的parent为nullptr,此时需要手动管理内存,如果parent为QObject不为空,
 在parent析构时,会销毁parent的所有child objects,QObject的数据私有类QObjectPrivate的基类QObjectData有一个成员变量QObjectList children;
 这是一个QList的typedef,定义为typedef QList<QObject*> QObjectList;该列表用来持有parent的所有child,在parent析构时,会遍历children列表,
 QObject的析构函数会调用QObjectPrivate::deleteChildren();函数定义如下
//E:\Qt\qt-everywhere-src-6.2.1\qtbase\src\corelib\kernel\qobject.cpp:2096
void QObjectPrivate::deleteChildren()
{
    Q_ASSERT_X(!isDeletingChildren, "QObjectPrivate::deleteChildren()", "isDeletingChildren already set, did this function recurse?");
    isDeletingChildren = true;
    // delete children objects
    // don't use qDeleteAll as the destructor of the child might
    // delete siblings
    for (int i = 0; i < children.count(); ++i) {
        currentChildBeingDeleted = children.at(i);
        children[i] = nullptr;
        delete currentChildBeingDeleted;
    }
    children.clear();
    currentChildBeingDeleted = nullptr;
    isDeletingChildren = false;
}
    在析构一个QObject对象时,还会检测该对象是否有parent对象,如果有,会调用void QObjectPrivate::setParent_helper(QObject *o)接口,传入
    的参数为nullptr,这样就将该对象从其父对象的children列表中移除了,说到这里大家会发现, 这里的父子对象关系跟C++类的继承派生关系是
    完全不一样的,Qt通过让一个QObject持有一个指向父亲的parent指针和指向子对象的children列表,构成了庞大的对象树,以此实现了半自动化
    的内存管理,当一个QDialog窗口析构时,上面的按钮、编辑框等子部件也会自动析构,非常符合图形界面程序的应用场景。
    
    2.贴一下void QObject::setParent(QObject *parent)
void QObject::setParent(QObject *parent)
{
    Q_D(QObject);
    Q_ASSERT(!d->isWidget);
    d->setParent_helper(parent);
}
void QObjectPrivate::setParent_helper(QObject *o)
{
    Q_Q(QObject);
    Q_ASSERT_X(q != o, Q_FUNC_INFO, "Cannot parent a QObject to itself");//不能设置QObjct的parent为自身
#ifdef QT_DEBUG
    const auto checkForParentChildLoops = qScopeGuard([&](){
        int depth = 0;
        auto p = parent;
        while (p) {
            if (++depth == CheckForParentChildLoopsWarnDepth) {
                qWarning("QObject %p (class: '%s', object name: '%s') may have a loop in its parent-child chain; "
                         "this is undefined behavior",
                         q, q->metaObject()->className(), qPrintable(q->objectName()));
            }
            p = p->parent();
        }
    });
#endif

    if (o == parent)
        return;

    if (parent) {
        QObjectPrivate *parentD = parent->d_func();
        if (parentD->isDeletingChildren && wasDeleted
            && parentD->currentChildBeingDeleted == q) {
            // don't do anything since QObjectPrivate::deleteChildren() already
            // cleared our entry in parentD->children.
        } else {
            const int index = parentD->children.indexOf(q);//获取在父对象children列表中的index
            if (index < 0) {
                // we're probably recursing into setParent() from a ChildRemoved event, don't do anything
            } else if (parentD->isDeletingChildren) {
                parentD->children[index] = nullptr;
            } else {
                parentD->children.removeAt(index); //从父对象children列表中移除自己
                if (sendChildEvents && parentD->receiveChildEvents) {
                    QChildEvent e(QEvent::ChildRemoved, q);
                    QCoreApplication::sendEvent(parent, &e);//如果允许发出子对象移除事件,在下一个事件循环中发出
                }//子对象移除事件,可以重载[virtual protected] void QObject::childEvent(QChildEvent *event),
            }//在子对象移除时做相应的处理
        }
    }
    parent = o;
    if (parent) {
        // object hierarchies are constrained to a single thread
        if (threadData != parent->d_func()->threadData) {
            qWarning("QObject::setParent: Cannot set parent, new parent is in a different thread");
            parent = nullptr;//threadData为数据类QObjectPrivate的成员变量,QAtomicPointer<QThreadData> threadData;
            return;//在QObject构造函数中赋值,唯一标志一个线程对象,parent->d_func()->threadData代表了新设置的parent
        }//所处的线程,在Qt中,父子对象必须处在同一个线程中。
        parent->d_func()->children.append(q);//将该对象加入到新parent的children列表中
        if (sendChildEvents && parent->d_func()->receiveChildEvents) {
            if (!isWidget) {
                QChildEvent e(QEvent::ChildAdded, q);
                QCoreApplication::sendEvent(parent, &e);//如果允许发出子对象添加事件,发出,同时会发出子对象添加信号
            }
        }
    }
}

二、线程亲和性(Thread Affinity)

        在QObject的构造函数中,在上面QObject的构造函数中,有如下代码:
    auto threadData = (parent && !parent->thread()) ? parent->d_func()->threadData.loadRelaxed() : 			       QThreadData::current();
    threadData->ref();
    d->threadData.storeRelaxed(threadData);
     在QObject的数据类QObjectPrivate中有如下定义:QAtomicPointer<QThreadData> threadData;   QThreadData类表征的是一个Qt线程所具备的
     功能、特征,里面的几个public数据成员,非常重要,是Qt线程和事件系统的核心,整个线程唯一。上面的代码表明,新构建一个QObject对象
     时,如果parent存在,取parent的threadData,否则,取创建该QObject所在线程的threadData。换言之,QObject的父子对象必须处在同一个线程
     中,每一个QObject对象都绑定在某一个线程中,QObject::setParent(QObject *parent)时,如果
     parent的threadData和子对象的threadData不同,就是设置失败;当我们执行
     void postEvent(QObject *receiver, QEvent *event, int priority = Qt::NormalEventPriority);将事件抛入事件循环时,就是将event事件添加到
     receiver的threadData中的postEventList事件列表中,换言之,receiver属于那个线程,postEvent时,事件就加入到哪个线程的事件循环,对比
     我们常用的[static] QMetaObject::Connection QObject::connect(const QObject *sender, const char *signal, const QObject *receiver, 
     const char *method, Qt::ConnectionType type = Qt::AutoConnection);当最后一个参数Qt::ConnectionType为Qt::QueuedConnection时,槽函数
     在接收者所在线程执行,当我们使用moveToThread()函数将接收者的线程亲和性转移到一个单独的线程时,此时槽函数就在该线程中执行,也就
     实现了将耗时任务放在工作线程中执行的目的了。
//E:\Qt\qt-everywhere-src-6.2.1\qtbase\src\corelib\thread\qthread_p.h:285
class QThreadData
{
public:
    QThreadData(int initialRefCount = 1);
    ~QThreadData();

    static Q_AUTOTEST_EXPORT QThreadData *current(bool createIfNecessary = true);
		....

public:
    int loopLevel;
    int scopeLevel;

    QStack<QEventLoop *> eventLoops; 	//嵌套的事件循环
    QPostEventList postEventList;	//整个线程的事件队列
    QAtomicPointer<QThread> thread; //本线程指针
    QAtomicPointer<void> threadId;	//线程id
    QAtomicPointer<QAbstractEventDispatcher> eventDispatcher; //负责具体的事件分发,平台相关
    QList<void *> tls;
    FlaggedDebugSignatures flaggedSignatures;

    bool quitNow;
    bool canWait;
    bool isAdopted;
    bool requiresCoreApplication;
};
  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值