温酒读Qt:QObject中篇2 ——欲遮还羞的 QObjectPrivate


《妙法莲华经》曰:“佛道长远,久受勤苦,乃可得成。” 世事修炼,莫不如是,日拱一卒无有尽,功不唐捐终入海。


传送门:
《温酒读Qt:QObject 序篇》
《温酒读Qt:QObject中篇1—— Q_OBJECT的隐秘角落》


1、QObjectPrivate class

先贴源码,然后我们挑重点进行分析;

/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Copyright (C) 2013 Olivier Goffart <ogoffart@woboq.com>
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/#ifndef QOBJECT_P_H
#define QOBJECT_P_H//
//  W A R N I N G
//  -------------
//
// This file is not part of the Qt API.  It exists for the convenience
// of qapplication_*.cpp, qwidget*.cpp and qfiledialog.cpp.  This header
// file may change from version to version without notice, or even be removed.
//
// We mean it.
//#include <QtCore/private/qglobal_p.h>
#include "QtCore/qobject.h"
#include "QtCore/qpointer.h"
#include "QtCore/qsharedpointer.h"
#include "QtCore/qcoreevent.h"
#include "QtCore/qlist.h"
#include "QtCore/qvector.h"
#include "QtCore/qvariant.h"
#include "QtCore/qreadwritelock.h"QT_BEGIN_NAMESPACEclass QVariant;
class QThreadData;
class QObjectConnectionListVector;
namespace QtSharedPointer { struct ExternalRefCountData; }/* for Qt Test */
struct QSignalSpyCallbackSet
{typedef void (*BeginCallback)(QObject *caller, int signal_or_method_index, void **argv);typedef void (*EndCallback)(QObject *caller, int signal_or_method_index);BeginCallback signal_begin_callback,slot_begin_callback;EndCallback signal_end_callback,slot_end_callback;
};
void Q_CORE_EXPORT qt_register_signal_spy_callbacks(QSignalSpyCallbackSet *callback_set);extern Q_CORE_EXPORT QBasicAtomicPointer<QSignalSpyCallbackSet> qt_signal_spy_callback_set;enum { QObjectPrivateVersion = QT_VERSION };class Q_CORE_EXPORT QAbstractDeclarativeData
{
public:static void (*destroyed)(QAbstractDeclarativeData *, QObject *);static void (*destroyed_qml1)(QAbstractDeclarativeData *, QObject *);static void (*parentChanged)(QAbstractDeclarativeData *, QObject *, QObject *);static void (*signalEmitted)(QAbstractDeclarativeData *, QObject *, int, void **);static int  (*receivers)(QAbstractDeclarativeData *, const QObject *, int);static bool (*isSignalConnected)(QAbstractDeclarativeData *, const QObject *, int);static void (*setWidgetParent)(QObject *, QObject *); // Used by the QML engine to specify parents for widgets. Set by QtWidgets.
};// This is an implementation of QAbstractDeclarativeData that is identical with
// the implementation in QtDeclarative and QtQml for the first bit
struct QAbstractDeclarativeDataImpl : public QAbstractDeclarativeData
{quint32 ownedByQml1:1;quint32 unused: 31;
};class Q_CORE_EXPORT QObjectPrivate : public QObjectData
{Q_DECLARE_PUBLIC(QObject)public:struct ExtraData{ExtraData() {}#ifndef QT_NO_USERDATAQVector<QObjectUserData *> userData;#endifQList<QByteArray> propertyNames;QVector<QVariant> propertyValues;QVector<int> runningTimers;QList<QPointer<QObject> > eventFilters;QString objectName;};typedef void (*StaticMetaCallFunction)(QObject *, QMetaObject::Call, int, void **);struct Connection;struct SignalVector;struct ConnectionOrSignalVector {union {// linked list of orphaned connections that need cleaning upConnectionOrSignalVector *nextInOrphanList;// linked list of connections connected to slots in this objectConnection *next;};static SignalVector *asSignalVector(ConnectionOrSignalVector *c) {if (reinterpret_cast<quintptr>(c) & 1)return reinterpret_cast<SignalVector *>(reinterpret_cast<quintptr>(c) & ~quintptr(1u));return nullptr;}static Connection *fromSignalVector(SignalVector *v) {return reinterpret_cast<Connection *>(reinterpret_cast<quintptr>(v) | quintptr(1u));}};struct Connection : public ConnectionOrSignalVector{// linked list of connections connected to slots in this object, next is in base classConnection **prev;// linked list of connections connected to signals in this objectQAtomicPointer<Connection> nextConnectionList;Connection *prevConnectionList;QObject *sender;QAtomicPointer<QObject> receiver;QAtomicPointer<QThreadData> receiverThreadData;union {StaticMetaCallFunction callFunction;QtPrivate::QSlotObjectBase *slotObj;};QAtomicPointer<const int> argumentTypes;QAtomicInt ref_;uint id = 0;ushort method_offset;ushort method_relative;int signal_index : 27; // In signal range (see QObjectPrivate::signalIndex())ushort connectionType : 3; // 0 == auto, 1 == direct, 2 == queued, 4 == blockingushort isSlotObject : 1;ushort ownArgumentTypes : 1;Connection() : ref_(2), ownArgumentTypes(true) {//ref_ is 2 for the use in the internal lists, and for the use in QMetaObject::Connection}~Connection();int method() const { Q_ASSERT(!isSlotObject); return method_offset + method_relative; }void ref() { ref_.ref(); }void freeSlotObject(){if (isSlotObject) {slotObj->destroyIfLastRef();isSlotObject = false;}}void deref() {if (!ref_.deref()) {Q_ASSERT(!receiver.loadRelaxed());Q_ASSERT(!isSlotObject);delete this;}}};// ConnectionList is a singly-linked liststruct ConnectionList {QAtomicPointer<Connection> first;QAtomicPointer<Connection> last;};struct Sender{Sender(QObject *receiver, QObject *sender, int signal): receiver(receiver), sender(sender), signal(signal){if (receiver) {ConnectionData *cd = receiver->d_func()->connections.loadRelaxed();previous = cd->currentSender;cd->currentSender = this;}}~Sender(){if (receiver)receiver->d_func()->connections.loadRelaxed()->currentSender = previous;}void receiverDeleted(){Sender *s = this;while (s) {s->receiver = nullptr;s = s->previous;}}Sender *previous;QObject *receiver;QObject *sender;int signal;};struct SignalVector : public ConnectionOrSignalVector {quintptr allocated;// ConnectionList signals[]ConnectionList &at(int i){return reinterpret_cast<ConnectionList *>(this + 1)[i + 1];}const ConnectionList &at(int i) const{return reinterpret_cast<const ConnectionList *>(this + 1)[i + 1];}int count() const { return static_cast<int>(allocated); }};/*This contains the all connections from and to an object.The signalVector contains the lists of connections for a given signal. The index in the vector correspondto the signal index. The signal index is the one returned by QObjectPrivate::signalIndex (notQMetaObject::indexOfSignal). allsignals contains a list of special connections that will get invoked onany signal emission. This is done by connecting to signal index -1.This vector is protected by the object mutex (signalSlotLock())Each Connection is also part of a 'senders' linked list. This one contains all connections connectedto a slot in this object. The mutex of the receiver must be locked when touching the pointers of thislinked list.*/struct ConnectionData {// the id below is used to avoid activating new connections. When the object gets// deleted it's set to 0, so that signal emission stopsQAtomicInteger<uint> currentConnectionId;QAtomicInt ref;QAtomicPointer<SignalVector> signalVector;Connection *senders = nullptr;Sender *currentSender = nullptr;   // object currently activating the objectQAtomicPointer<Connection> orphaned;~ConnectionData(){deleteOrphaned(orphaned.loadRelaxed());SignalVector *v = signalVector.loadRelaxed();if (v)free(v);}// must be called on the senders connection data// assumes the senders and receivers lock are heldvoid removeConnection(Connection *c);void cleanOrphanedConnections(QObject *sender){if (orphaned.loadRelaxed() && ref.loadAcquire() == 1)cleanOrphanedConnectionsImpl(sender);}void cleanOrphanedConnectionsImpl(QObject *sender);ConnectionList &connectionsForSignal(int signal){return signalVector.loadRelaxed()->at(signal);}void resizeSignalVector(uint size) {SignalVector *vector = this->signalVector.loadRelaxed();if (vector && vector->allocated > size)return;size = (size + 7) & ~7;SignalVector *newVector = reinterpret_cast<SignalVector *>(malloc(sizeof(SignalVector) + (size + 1) * sizeof(ConnectionList)));int start = -1;if (vector) {memcpy(newVector, vector, sizeof(SignalVector) + (vector->allocated + 1) * sizeof(ConnectionList));start = vector->count();}for (int i = start; i < int(size); ++i)newVector->at(i) = ConnectionList();newVector->next = nullptr;newVector->allocated = size;signalVector.storeRelaxed(newVector);if (vector) {vector->nextInOrphanList = orphaned.loadRelaxed();orphaned.storeRelaxed(ConnectionOrSignalVector::fromSignalVector(vector));}}int signalVectorCount() const {return  signalVector.loadAcquire() ? signalVector.loadRelaxed()->count() : -1;}static void deleteOrphaned(ConnectionOrSignalVector *c);};QObjectPrivate(int version = QObjectPrivateVersion);virtual ~QObjectPrivate();void deleteChildren();void setParent_helper(QObject *);void moveToThread_helper();void setThreadData_helper(QThreadData *currentData, QThreadData *targetData);void _q_reregisterTimers(void *pointer);bool isSender(const QObject *receiver, const char *signal) const;QObjectList receiverList(const char *signal) const;QObjectList senderList() const;void addConnection(int signal, Connection *c);static QObjectPrivate *get(QObject *o) {return o->d_func();}static const QObjectPrivate *get(const QObject *o) { return o->d_func(); }int signalIndex(const char *signalName, const QMetaObject **meta = nullptr) const;bool isSignalConnected(uint signalIdx, bool checkDeclarative = true) const;bool maybeSignalConnected(uint signalIndex) const;inline bool isDeclarativeSignalConnected(uint signalIdx) const;// To allow abitrary objects to call connectNotify()/disconnectNotify() without making// the API public in QObject. This is used by QQmlNotifierEndpoint.inline void connectNotify(const QMetaMethod &signal);inline void disconnectNotify(const QMetaMethod &signal);template <typename Func1, typename Func2>static inline QMetaObject::Connection connect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal,const typename QtPrivate::FunctionPointer<Func2>::Object *receiverPrivate, Func2 slot,Qt::ConnectionType type = Qt::AutoConnection);template <typename Func1, typename Func2>static inline bool disconnect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal,const typename QtPrivate::FunctionPointer<Func2>::Object *receiverPrivate, Func2 slot);static QMetaObject::Connection connectImpl(const QObject *sender, int signal_index,const QObject *receiver, void **slot,QtPrivate::QSlotObjectBase *slotObj, Qt::ConnectionType type,const int *types, const QMetaObject *senderMetaObject);static QMetaObject::Connection connect(const QObject *sender, int signal_index, QtPrivate::QSlotObjectBase *slotObj, Qt::ConnectionType type);static bool disconnect(const QObject *sender, int signal_index, void **slot);void ensureConnectionData(){if (connections.loadRelaxed())return;ConnectionData *cd = new ConnectionData;cd->ref.ref();connections.storeRelaxed(cd);}
public:ExtraData *extraData;    // extra data set by the userQThreadData *threadData; // id of the thread that owns the objectusing ConnectionDataPointer = QExplicitlySharedDataPointer<ConnectionData>;QAtomicPointer<ConnectionData> connections;union {QObject *currentChildBeingDeleted; // should only be used when QObjectData::isDeletingChildren is setQAbstractDeclarativeData *declarativeData; //extra data used by the declarative module};// these objects are all used to indicate that a QObject was deleted// plus QPointer, which keeps a separate listQAtomicPointer<QtSharedPointer::ExternalRefCountData> sharedRefcount;
};Q_DECLARE_TYPEINFO(QObjectPrivate::ConnectionList, Q_MOVABLE_TYPE);inline bool QObjectPrivate::isDeclarativeSignalConnected(uint signal_index) const
{return declarativeData && QAbstractDeclarativeData::isSignalConnected&& QAbstractDeclarativeData::isSignalConnected(declarativeData, q_func(), signal_index);
}inline void QObjectPrivate::connectNotify(const QMetaMethod &signal)
{q_ptr->connectNotify(signal);
}inline void QObjectPrivate::disconnectNotify(const QMetaMethod &signal)
{q_ptr->disconnectNotify(signal);
}namespace QtPrivate {
template<typename Func, typename Args, typename R> class QPrivateSlotObject : public QSlotObjectBase
{typedef QtPrivate::FunctionPointer<Func> FuncType;Func function;static void impl(int which, QSlotObjectBase *this_, QObject *r, void **a, bool *ret){switch (which) {case Destroy:delete static_cast<QPrivateSlotObject*>(this_);break;case Call:FuncType::template call<Args, R>(static_cast<QPrivateSlotObject*>(this_)->function,static_cast<typename FuncType::Object *>(QObjectPrivate::get(r)), a);break;case Compare:*ret = *reinterpret_cast<Func *>(a) == static_cast<QPrivateSlotObject*>(this_)->function;break;case NumOperations: ;}}
public:explicit QPrivateSlotObject(Func f) : QSlotObjectBase(&impl), function(f) {}
};
} //namespace QtPrivatetemplate <typename Func1, typename Func2>
inline QMetaObject::Connection QObjectPrivate::connect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal,const typename QtPrivate::FunctionPointer<Func2>::Object *receiverPrivate, Func2 slot,Qt::ConnectionType type)
{typedef QtPrivate::FunctionPointer<Func1> SignalType;typedef QtPrivate::FunctionPointer<Func2> SlotType;Q_STATIC_ASSERT_X(QtPrivate::HasQ_OBJECT_Macro<typename SignalType::Object>::Value,"No Q_OBJECT in the class with the signal");//compilation error if the arguments does not match.Q_STATIC_ASSERT_X(int(SignalType::ArgumentCount) >= int(SlotType::ArgumentCount),"The slot requires more arguments than the signal provides.");Q_STATIC_ASSERT_X((QtPrivate::CheckCompatibleArguments<typename SignalType::Arguments, typename SlotType::Arguments>::value),"Signal and slot arguments are not compatible.");Q_STATIC_ASSERT_X((QtPrivate::AreArgumentsCompatible<typename SlotType::ReturnType, typename SignalType::ReturnType>::value),"Return type of the slot is not compatible with the return type of the signal.");const int *types = nullptr;if (type == Qt::QueuedConnection || type == Qt::BlockingQueuedConnection)types = QtPrivate::ConnectionTypes<typename SignalType::Arguments>::types();return QObject::connectImpl(sender, reinterpret_cast<void **>(&signal),receiverPrivate->q_ptr, reinterpret_cast<void **>(&slot),new QtPrivate::QPrivateSlotObject<Func2, typename QtPrivate::List_Left<typename SignalType::Arguments, SlotType::ArgumentCount>::Value,typename SignalType::ReturnType>(slot),type, types, &SignalType::Object::staticMetaObject);
}template <typename Func1, typename Func2>
bool QObjectPrivate::disconnect(const typename QtPrivate::FunctionPointer< Func1 >::Object* sender, Func1 signal,const typename QtPrivate::FunctionPointer< Func2 >::Object* receiverPrivate, Func2 slot)
{typedef QtPrivate::FunctionPointer<Func1> SignalType;typedef QtPrivate::FunctionPointer<Func2> SlotType;Q_STATIC_ASSERT_X(QtPrivate::HasQ_OBJECT_Macro<typename SignalType::Object>::Value,"No Q_OBJECT in the class with the signal");//compilation error if the arguments does not match.Q_STATIC_ASSERT_X((QtPrivate::CheckCompatibleArguments<typename SignalType::Arguments, typename SlotType::Arguments>::value),"Signal and slot arguments are not compatible.");return QObject::disconnectImpl(sender, reinterpret_cast<void **>(&signal),receiverPrivate->q_ptr, reinterpret_cast<void **>(&slot),&SignalType::Object::staticMetaObject);
}Q_DECLARE_TYPEINFO(QObjectPrivate::Connection, Q_MOVABLE_TYPE);
Q_DECLARE_TYPEINFO(QObjectPrivate::Sender, Q_MOVABLE_TYPE);class QSemaphore;
class Q_CORE_EXPORT QAbstractMetaCallEvent : public QEvent
{
public:QAbstractMetaCallEvent(const QObject *sender, int signalId, QSemaphore *semaphore = nullptr): QEvent(MetaCall), signalId_(signalId), sender_(sender)
#if QT_CONFIG(thread), semaphore_(semaphore)
#endif{ Q_UNUSED(semaphore); }~QAbstractMetaCallEvent();virtual void placeMetaCall(QObject *object) = 0;inline const QObject *sender() const { return sender_; }inline int signalId() const { return signalId_; }private:int signalId_;const QObject *sender_;
#if QT_CONFIG(thread)QSemaphore *semaphore_;
#endif
};class Q_CORE_EXPORT QMetaCallEvent : public QAbstractMetaCallEvent
{
public:// blocking queued with semaphore - args always owned by callerQMetaCallEvent(ushort method_offset, ushort method_relative,QObjectPrivate::StaticMetaCallFunction callFunction,const QObject *sender, int signalId,void **args, QSemaphore *semaphore);QMetaCallEvent(QtPrivate::QSlotObjectBase *slotObj,const QObject *sender, int signalId,void **args, QSemaphore *semaphore);// queued - args allocated by event, copied by callerQMetaCallEvent(ushort method_offset, ushort method_relative,QObjectPrivate::StaticMetaCallFunction callFunction,const QObject *sender, int signalId,int nargs);QMetaCallEvent(QtPrivate::QSlotObjectBase *slotObj,const QObject *sender, int signalId,int nargs);~QMetaCallEvent() override;inline int id() const { return d.method_offset_ + d.method_relative_; }inline const void * const* args() const { return d.args_; }inline void ** args() { return d.args_; }inline const int *types() const { return reinterpret_cast<int*>(d.args_ + d.nargs_); }inline int *types() { return reinterpret_cast<int*>(d.args_ + d.nargs_); }virtual void placeMetaCall(QObject *object) override;private:inline void allocArgs();struct Data {QtPrivate::QSlotObjectBase *slotObj_;void **args_;QObjectPrivate::StaticMetaCallFunction callFunction_;int nargs_;ushort method_offset_;ushort method_relative_;} d;// preallocate enough space for three argumentschar prealloc_[3*(sizeof(void*) + sizeof(int))];
};class QBoolBlocker
{Q_DISABLE_COPY_MOVE(QBoolBlocker)
public:explicit inline QBoolBlocker(bool &b, bool value=true):block(b), reset(b){block = value;}inline ~QBoolBlocker(){block = reset; }
private:bool &block;bool reset;
};void Q_CORE_EXPORT qDeleteInEventHandler(QObject *o);struct QAbstractDynamicMetaObject;
struct Q_CORE_EXPORT QDynamicMetaObjectData
{virtual ~QDynamicMetaObjectData();virtual void objectDestroyed(QObject *) { delete this; }virtual QAbstractDynamicMetaObject *toDynamicMetaObject(QObject *) = 0;virtual int metaCall(QObject *, QMetaObject::Call, int _id, void **) = 0;
};struct Q_CORE_EXPORT QAbstractDynamicMetaObject : public QDynamicMetaObjectData, public QMetaObject
{~QAbstractDynamicMetaObject();QAbstractDynamicMetaObject *toDynamicMetaObject(QObject *) override { return this; }virtual int createProperty(const char *, const char *) { return -1; }int metaCall(QObject *, QMetaObject::Call c, int _id, void **a) override{ return metaCall(c, _id, a); }virtual int metaCall(QMetaObject::Call, int _id, void **) { return _id; } // Compat overload
};QT_END_NAMESPACE#endif // QOBJECT_P_H

代码量也不多,不过很多朋友对于看源码这件事情,刚开始看到这一坨代码可能内心莫名的就会生出反感的情绪来。这里说说博主的方法哈。
在这里插入图片描述
我们基于Qt的框架开发,特别是在刚接触的情况下,最有效和快速准确的熟悉Qt模块/class/API等的方法,肯定是查看帮助手册。而且我也不止一次的表扬过Qt的Assistant, 写的是真的好,条理清晰,一目了然。 只不过,我们现在调试源码,你在帮助手册中去查看QObjectPrivate,这定然是无法查找到的。但是,帮助手册中的目录却值得借鉴。

首先,明确我们当前目标为 QObject 的私有类QObjectPrivate,我们重点要关注以下几点

  • 1、QObjectPrivate 是什么,主要有什么作用?
  • 2、QObjectPrivate 的继承关系?(这个很重要,能帮你在后面快速厘清成员变量和成员函数,以及哪些是重写的函数,以及哪些是自己特有的函数)
  • 3、它包含哪些成员变量成员函数?大概起到了什么作用?

从这个角度来梳理,那我们就相对比较容易了。
让大家觉得不容易的,可能就是在查看源码的过程中包含了各种语法糖,各种看着就很高级的,不知如何下手。不过反过来思考,你暂时看着、理解着难受的,不都是你未知,并可以前进的方向么,这是一件很开心的事情啊。博主奉行的就是“终生学习”的理念,对于未知常常会充满兴趣,并决心去愉快的探索。

2、继承关系

QObjectPrivate -> QObjectData

继承的深度只有一层,舒服,简单搂一眼QObjectData

class Q_CORE_EXPORT QObjectData {Q_DISABLE_COPY(QObjectData)
public:QObjectData() = default;virtual ~QObjectData() = 0; // 这个接口表明了QObjectData是一个接口类,只能被继承QObject *q_ptr;				// 原来q指针刺客在这里申明的QObject *parent;			// 每个QObject的对象或者子类对象都有记住父类的对象指针,那么在查找某个对象的子类对象时就轻而易举了QObjectList children;   	// typedef QList<QObject*> QObjectList;  这里更直接,直接记录了该对象的所有子类对象;每个子类对象展开又包含有同样的结构,说说看这是什么,可不就是一个嵌套的链式结构么uint isWidget : 1;			// 位域的知识不多说uint blockSig : 1;uint wasDeleted : 1;uint isDeletingChildren : 1;uint sendChildEvents : 1;uint receiveChildEvents : 1;uint isWindow : 1; //for QWindowuint deleteLaterCalled : 1;uint unused : 24;int postedEvents;QDynamicMetaObjectData *metaObject;QMetaObject *dynamicMetaObject() const;#ifdef QT_DEBUGenum { CheckForParentChildLoopsWarnDepth = 4096 };
#endif
};

看完了QObjectData 的数据结构,其实我们有一个很有趣的问题引出来。

我们都知道Qt中所有的对象都继承自QObject,譬如QWidget也是继承自QObject 的。我们在编码的过程中,为什么实例化QObject 的子类对象,我们只说在堆上为对象申请内存的情况,为何却不需要自己编码去显示调用对象内存释放函数,来销毁对象实例呢?

问题抛出来,我们会在循序渐进的过程中解答这个问题~ 当然,如果是已经看到这儿的朋友,你怀有强烈的好奇心,也不妨自己动动手去寻找答案(注释中其实已经给了提示了哈)

3、成员变量

从代码中找成员变量,有时候代码行数比较多,且成员变量的定义比较分散的时候,确实不宜直接查看。这时候我们得想想办法。
在最近的 《2024了,我不允许你还不会:Qt查看与调试源码》一文中,同大家分享了Qt源码调试环境的搭建技巧。这里我们也是写一段简单的代码,通过调试器来辅助我们查看源码:

调试代码:

#include <QCoreApplication>
#include <QObject>int main(int argc, char *argv[])
{QCoreApplication a(argc, argv);QObject o1,o2;QObject::connect(&o1,&QObject::destroyed, &o2,&QObject::destroyed);return a.exec();
}

我们始终要调试的时QObject 对吧,我们也说了 QObject QObjectPrivate的关系,对吧。
好的,那我们如下打两个断点。然后开始愉快的探索之旅。

在这里插入图片描述

如上,我们实例化了两个QObject的对象,分别是o1o2;
在这里插入图片描述
我们看到,QObject的实例化对象o1中包含了4个成员变量,分别是vptrd_ptr 以及staticMetaObjectstaticQtMetaObject .

  • vptr 从何而来? 我们回归下C++的基础知识,QObject 是一个虚类,当实例化对象时,编译器会自动为实例化对象添加一个内置的成员变量 vptr(即我们常说的虚表指针,指向QObject 类在编译时生成的虚函数表)。
  • d_ptr 是啥? 我们在源码中很快可以找到其定义:QScopedPointer<QObjectData> d_ptr; 指向 QObjectData数据成员变量的智能指针。
  • staticQtMetaObject 是怎么定义的? static const QMetaObject staticQtMetaObject; 即元对象类型的静态常成员变量。
  • staticMetaObject 呢?同上。

我们回到本章主题,在来看看 QObjectPrivate.
前面我们已经说过,QObjectPrivate 公有继承自 QObjectData,在QObject类中定义了QScopedPointer<QObjectData> d_ptr;
QObject的构造函数里 ,定义了d_ptr;

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

所以,这里就是我们常说的“向上造型”,用基类(QObjectData)指针 指向子类(QObjectPrivate)对象.

我们再借助调试器来看看 QObjectPrivate实例化对象的布局:
在这里插入图片描述
因为 QObjectPrivate 共有继承自QObjectData,所以,它可以访问 QObjectData的所有成员变量(QObjectData 中所有成员变量都是 public属性的)。
其次,它还含有如下几个成员变量:

  • currentChildBeingDeleted :
  • declarativeData
    这俩哥们被定义在一个联合体中,即两个存在互斥关系。
union {QObject *currentChildBeingDeleted; // should only be used when QObjectData::isDeletingChildren is setQAbstractDeclarativeData *declarativeData; //extra data used by the declarative module
};
  • extraData : extra data set by the user
  • shareRefcount : these objects are all used to indicate that a QObject was deleted plus QPointer, which keeps a separate list
QAtomicPointer<QtSharedPointer::ExternalRefCountData> sharedRefcount;
  • threadData : id of the thread that owns the object

  • connections : 记录信号和槽的连接信息

QAtomicPointer<ConnectionData> connections;

我特意把 connections 放到下面来说,因为这里非常有意思。继续往下挖,就到了Qt信号和槽机制的了,黑大粗那种。看看 ConnectionData 类:

struct ConnectionData {// the id below is used to avoid activating new connections. When the object gets// deleted it's set to 0, so that signal emission stopsQAtomicInteger<uint> currentConnectionId;QAtomicInt ref;  QAtomicPointer<SignalVector> signalVector;  /// 信号容器Connection *senders = nullptr;Sender *currentSender = nullptr;   // object currently activating the objectQAtomicPointer<Connection> orphaned;~ConnectionData(){deleteOrphaned(orphaned.loadRelaxed());SignalVector *v = signalVector.loadRelaxed();if (v)free(v);}// must be called on the senders connection data// assumes the senders and receivers lock are heldvoid removeConnection(Connection *c);void cleanOrphanedConnections(QObject *sender){if (orphaned.loadRelaxed() && ref.loadAcquire() == 1)cleanOrphanedConnectionsImpl(sender);}void cleanOrphanedConnectionsImpl(QObject *sender);ConnectionList &connectionsForSignal(int signal){return signalVector.loadRelaxed()->at(signal);}void resizeSignalVector(uint size) {SignalVector *vector = this->signalVector.loadRelaxed();if (vector && vector->allocated > size)return;size = (size + 7) & ~7;SignalVector *newVector = reinterpret_cast<SignalVector *>(malloc(sizeof(SignalVector) + (size + 1) * sizeof(ConnectionList)));int start = -1;if (vector) {memcpy(newVector, vector, sizeof(SignalVector) + (vector->allocated + 1) * sizeof(ConnectionList));start = vector->count();}for (int i = start; i < int(size); ++i)newVector->at(i) = ConnectionList();newVector->next = nullptr;newVector->allocated = size;signalVector.storeRelaxed(newVector);if (vector) {vector->nextInOrphanList = orphaned.loadRelaxed();orphaned.storeRelaxed(ConnectionOrSignalVector::fromSignalVector(vector));}}int signalVectorCount() const {return  signalVector.loadAcquire() ? signalVector.loadRelaxed()->count() : -1;}static void deleteOrphaned(ConnectionOrSignalVector *c);
};
struct ConnectionOrSignalVector {union {// linked list of orphaned connections that need cleaning up// 需要清理的孤立连接的链接列表ConnectionOrSignalVector *nextInOrphanList;// linked list of connections connected to slots in this object// 连接到此对象中插槽的连接的链接列表Connection *next;};static SignalVector *asSignalVector(ConnectionOrSignalVector *c) {if (reinterpret_cast<quintptr>(c) & 1)return reinterpret_cast<SignalVector *>(reinterpret_cast<quintptr>(c) & ~quintptr(1u));return nullptr;}static Connection *fromSignalVector(SignalVector *v) {return reinterpret_cast<Connection *>(reinterpret_cast<quintptr>(v) | quintptr(1u));}};struct SignalVector : public ConnectionOrSignalVector {quintptr allocated;// ConnectionList signals[]ConnectionList &at(int i){return reinterpret_cast<ConnectionList *>(this + 1)[i + 1];}const ConnectionList &at(int i) const{return reinterpret_cast<const ConnectionList *>(this + 1)[i + 1];}int count() const { return static_cast<int>(allocated); }
};
 // ConnectionList is a singly-linked liststruct ConnectionList {QAtomicPointer<Connection> first;QAtomicPointer<Connection> last;};

ConnectionList 是一个单链表;每个节点都是一个Connection 对象。

 struct Connection : public ConnectionOrSignalVector{// linked list of connections connected to slots in this object, next is in base classConnection **prev;// linked list of connections connected to signals in this objectQAtomicPointer<Connection> nextConnectionList;Connection *prevConnectionList;QObject *sender;    // 发送者对象QAtomicPointer<QObject> receiver; // 接收者对象QAtomicPointer<QThreadData> receiverThreadData; // 接收者线程id/**typedef void (*StaticMetaCallFunction)(QObject *, QMetaObject::Call, int, void **);// internal base class (interface) containing functions required to call a slot managed by a pointer to function.class QSlotObjectBase {QAtomicInt m_ref;// don't use virtual functions here; we don't want the// compiler to create tons of per-polymorphic-class stuff that// we'll never need. We just use one function pointer.typedef void (*ImplFn)(int which, QSlotObjectBase* this_, QObject *receiver, void **args, bool *ret);const ImplFn m_impl;protected:enum Operation {Destroy,Call,Compare,NumOperations};public:explicit QSlotObjectBase(ImplFn fn) : m_ref(1), m_impl(fn) {}inline int ref() noexcept { return m_ref.ref(); }inline void destroyIfLastRef() noexcept{ if (!m_ref.deref()) m_impl(Destroy, this, nullptr, nullptr, nullptr); }inline bool compare(void **a) { bool ret = false; m_impl(Compare, this, nullptr, a, &ret); return ret; }inline void call(QObject *r, void **a)  { m_impl(Call,    this, r, a, nullptr); }protected:~QSlotObjectBase() {}private:Q_DISABLE_COPY_MOVE(QSlotObjectBase)};*//// 定义了连接绑定的槽函数union {StaticMetaCallFunction callFunction;QtPrivate::QSlotObjectBase *slotObj;};QAtomicPointer<const int> argumentTypes;QAtomicInt ref_;uint id = 0;ushort method_offset;ushort method_relative;int signal_index : 27; // In signal range (see QObjectPrivate::signalIndex())ushort connectionType : 3; // 0 == auto, 1 == direct, 2 == queued, 4 == blockingushort isSlotObject : 1;ushort ownArgumentTypes : 1;Connection() : ref_(2), ownArgumentTypes(true) {//ref_ is 2 for the use in the internal lists, and for the use in QMetaObject::Connection}~Connection();int method() const { Q_ASSERT(!isSlotObject); return method_offset + method_relative; }void ref() { ref_.ref(); }void freeSlotObject(){if (isSlotObject) {slotObj->destroyIfLastRef();isSlotObject = false;}}void deref() {if (!ref_.deref()) {Q_ASSERT(!receiver.loadRelaxed());Q_ASSERT(!isSlotObject);delete this;}}
};

Connection 对象中定义了对象连接的信号和槽:

  • 一个对象既可以定义成信号的发送者,也可以定义成信号的接收者
  • 一个对象可以接收多个信号
  • 一个发送者对象可以绑定多个接收者信号及对应的槽函数

我们再回头看两个函数:

// Used by QAccessibleWidget
QObjectList QObjectPrivate::receiverList(const char *signal) const
{QObjectList returnValue;int signal_index = signalIndex(signal);ConnectionData *cd = connections.loadRelaxed();if (signal_index < 0 || !cd)return returnValue;if (signal_index < cd->signalVectorCount()) {const QObjectPrivate::Connection *c = cd->signalVector.loadRelaxed()->at(signal_index).first.loadRelaxed();while (c) {QObject *r = c->receiver.loadRelaxed();if (r)returnValue << r;c = c->nextConnectionList.loadRelaxed();}}return returnValue;
}// Used by QAccessibleWidget
QObjectList QObjectPrivate::senderList() const
{QObjectList returnValue;ConnectionData *cd = connections.loadRelaxed();if (cd) {QBasicMutexLocker locker(signalSlotLock(q_func()));for (Connection *c = cd->senders; c; c = c->next)returnValue << c->sender;}return returnValue;
}

看到这里,我们对于QObject 中对象数据类 QObjectPrivate 有了一些认识了。

楼已高,起下篇~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/439836.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

设计模式——模板方法模式(Template Method Pattern)

概述 模板方法模式&#xff1a;定义一个操作中算法的框架&#xff0c;而将一些步骤延迟到子类中。模板方法模式使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。模板方法模式是一种基于继承的代码复用技术&#xff0c;它是一种类行为型模式。模板方法模式是结…

Android中下载 HAXM 报错 Intel® HAXM installation failed,如何解决?

最近在搭建 Flutter 环境&#xff0c;但是在 Android Studio 中安装 Virtual Device 时&#xff0c;出现了一个 问题 Intel HAXM installation failed. To install Intel HAXM follow the instructions found at: https://github.com/intel/haxm/wiki/Installation-Instructio…

1.25时间序列分析,FB先知模型、简要傅里叶变化解决周期性变化,实例步骤

目录 FB概念 ​编辑 GEOGEBRA可视化傅里叶​编辑 先知模型步骤 财务数据要考虑到可解释性 FB模型概念 可以用傅里叶级数来描述周期性变化的因素 GEOGEBRA可视化傅里叶 先知模型步骤

安装好IntelliJ IDEA点击无反应,如何解决配置文件不一致导致的启动问题

在我们的开发生涯中&#xff0c;遇到IDE工具出现问题是在所难免的。最令人头疼的莫过于&#xff0c;你的IDEA(IntelliJ IDEA)无法启动&#xff0c;而且没有任何错误提示。这篇文章将详细讲解如何解决IntelliJ IDEA 2023.3.3版本启动失败的问题&#xff0c;这个问题可能也适用于…

如何让wordpress首页只显示某一篇文章全部内容?在您的主页显示选择

大多数WordPress站点首页默认都是显示最新发布的文章列表&#xff0c;不过有些站点比较特殊&#xff0c;只想显示某一篇文章的全部内容&#xff0c;那么应该怎么设置呢&#xff1f; 其实&#xff0c;WordPress后台 >> 设置 >> 阅读 >> 在“您的主页显示”中…

OpenHarmony RK3568 启动流程优化

目前rk3568的开机时间有21s&#xff0c;统计的是关机后从按下 power 按键到显示锁屏的时间&#xff0c;当对openharmony的系统进行了裁剪子系统&#xff0c;系统app&#xff0c;禁用部分服务后发现开机时间仅仅提高到了20.94s 优化微乎其微。在对init进程的log进行分析并解决其…

解决Linux环境下gdal报错:ERROR 4: `/xxx.hdf‘ not recognized as a supported file format.

网上查了一堆资料&#xff0c;五花八门&#xff0c;总结了一下可能的原因&#xff1a; ① gdal不支持该格式 使用命令“gdalinfo --formats” 即可查看当前环境中的gdal所能支持的数据格式。如下图&#xff08;没截完整&#xff0c;下面还有一大串&#xff09;。 这个是很常见…

分布式虚拟文件系统,如何实现多种存储系统的融合

随着大数据技术和人工智能技术的发展&#xff0c;各种框架应运而生&#xff0c;比如大数据领域中的MapReduce和Spark&#xff0c;人工智能领域中的TensorFlow和PyTorch等。为了给不同的计算框架提供存储服务&#xff0c;存储的服务类型也是很多&#xff0c;常见的如AWS的S3存储…

Docker(第三部分)

1&#xff0c;Docker复杂安装说明 今天的优势会被明天趋势所取代 一切在云端 安装mysql主从复制 主从复制原理&#xff0c;默认你懂 主从搭建步骤 1&#xff0c;新建主从服务器容器实例3307 docker run -p 3307:3306 --name mysql-master\ -v /mydata/mysql-master/log:…

设计模式_观察者模式_Observer

案例引入 有一个天气预报项目&#xff0c;需求如下&#xff1a; 气象站可以将每天测量到的温度、湿度、气压等等以公告的形式发布出去(比如发布到自己的网站或第三方)需要设计开放型API&#xff0c;便于其他第三方也能接入气象站获取数据提供温度、气压、湿度的接口测量数据更…

Java规则引擎:实现高效SQL变量数据处理的关键

SQL变量加工 SQL加工背景&#xff0c;在决策配置过程中&#xff0c;一些复杂的逻辑或模型可通过自定义SQL脚本编写创建数据变量&#xff0c;通过SQL脚本可以便捷的从数据库中取数&#xff0c;并且自定义SQL支持传参&#xff0c;可满足更复杂多变的数据加工处理。 注意&#x…

蓝桥杯2024/1/28----十二届省赛题笔记

题目要求&#xff1a; 2、 竞赛板配置要求 2.1将 IAP15F2K61S2 单片机内部振荡器频率设定为 12MHz。 2.2键盘工作模式跳线 J5 配置为 KBD 键盘模式。 2.3扩展方式跳线 J13 配置为 IO 模式。 2.4 请注意 &#xff1a; 选手需严格按照以上要求配置竞赛板&#xff0c;编写和调…