温酒读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.mzph.cn/news/655669.shtml

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

相关文章

前端框架---Vue2学习教程(上)

从HTML到现在一路跟过来的小伙伴们&#xff0c;坚持固然不容易&#xff0c;但我相信大家已经学到了不少&#xff0c;那么我们开始马不停蹄的进入前端的框架吧&#xff0c;下面讲的是Vue2&#xff0c;大家继续加油鸭&#xff01;&#xff01;&#xff01;&#xff01; Vue2 Vu…

装机打不开BIOS怎么办?如何进入Windows10的BIOS页面,如何关闭快速启动

电脑有快速启动&#xff0c;想进去BIOS页面非常困难&#xff0c;在临开机的页面&#xff0c;按触发按键不管用。 然后我看到了一种新的进入BIOS的方式&#xff1a; &#xff08;1&#xff09;win8以上的系统&#xff0c;按住shift&#xff0c;然后鼠标点击重启&#xff0c;再…

蓝桥杯2024/1/26笔记-----基于PCF8591的电压采集装置

功能实现要求&#xff1a; 每次建好工程文件夹&#xff0c;里边包含User&#xff08;放工程文件&#xff0c;mian.c&#xff0c;可以在这里写如同我这个文章的文本文档&#xff09;、Driver&#xff08;存放底层文件如Led.c&#xff0c;Led.h等&#xff09; 新建的工程先搭建框…

推荐一款Linux、数据库、Redis、MongoDB统一管理平台!

官方演示 状态查看 ssh 终端 文件操作 数据库操作 sql 编辑器 在线增删改查数据 Redis 操作 Mongo 操作 系统管理 账号管理 角色管理 资源管理 一.安装 1.下载安装包 cd /opt wget https://gitee.com/dromara/mayfly-go/releases/download/v1.7.1/mayfly-go-linux-amd64.zi…

CES 2024:AI赋能机器人,国产机器人更亮眼

原创 | 文 BFT机器人 一年一度的国际消费电子展(CES)又与我们见面了。作为全球消费电子和科技创新的盛会&#xff0c;CES每年都吸引着无数目光。今年&#xff0c;AI赋能机器人成为展会的一大亮点&#xff0c;而国产机器人更是凭借其创新技术和实用功能&#xff0c;成为全场焦点…

使用QT实现播放gstreamer的命令(二)

一、前言 上一篇文章写到了&#xff0c;如何快速使用C来执行gstreamer的命令&#xff0c;如何在QT中显示gstreamer的画面&#xff0c;原文如下&#xff1a; https://blog.csdn.net/Alon1787/article/details/135107958 二、近期的其他发现&#xff1a; 1.gstreamer的画面显示在…

蓝桥杯AT24C02问题记录

问题1&#xff1a;从这个图片上可以看出这两个在IIC的.c文件里延时时间不一样&#xff0c;第一张图使用了15个_nop_(); 12M晶振机器周期是 1/12M*121uS&#xff1b;nop()要延时1个指令周期。延时时间不对会对时序产生影响&#xff0c;时序不对&#xff0c;则AT24C02有没被使用…

关于Spring Boot和MyBatis常见的十道面试题

拦截器和过滤器有什么区别&#xff1f; 拦截器&#xff08;Interceptor&#xff09;和过滤器&#xff08;Filter&#xff09;都是用于在请求道道目标资源的之前或之后进行处理的组件。主要区别有以下几点&#xff1a; 依赖对象不同&#xff1a;过滤器是来时Servlet&#xff0…

IDEA安装MyBatisX插件

IDEA工具在开发人员中经常使用&#xff0c;从dao层到xml文件对应的查看很费劲&#xff0c;这时候就有相应的插件工具出现了MyBatisX。他的好处如下&#xff1a; mapper and xml can jump back and forth mybatis.xml,mapper.xml prompt mapper and xml support auto prompt lik…

Netty核心——Reactor下篇(十)

任务队列中的Task有3种典型使用场景 用户程序自定义的普通任务 比如有一个非常耗时长的业务 异步执行提交该Channel对应的NioEventLoop的TaskQueue中 用户自定义定时任务 该任务提交到scheduleTaskQueue中 非当前Reactor线程调用Channel的各种方法 例如在推送系统的业务线程…

大数据StarRocks(八):资源隔离实战

前言 自 2.2 版本起&#xff0c;StarRocks 支持资源组管理&#xff0c;集群可以通过设置资源组&#xff08;Resource Group&#xff09;的方式限制查询对资源的消耗&#xff0c;实现多租户之间的资源隔离与合理利用。在 2.3 版本中&#xff0c;StarRocks 支持限制大查询&#…

酒鬼酒2024年展望:稳发展动能,迈入恢复性增长轨道

文 | 琥珀酒研社 作者 | 渡过 最近几个月来&#xff0c;白酒估值回落到近十年来低位&#xff0c;反映出了整个白酒行业的市场低迷和虚弱现状。不管是头部企业五粮液、泸州老窖&#xff0c;还是区域酒企口子窖、金种子酒等&#xff0c;最近都通过“回购”或“增持”&#xff0…

常用直线检测算法

概述 在计算机视觉领域&#xff0c;我们经常需要做一些特殊的任务&#xff0c;而这些任务中经常会用到直线检测算法&#xff0c;比如车道线检测、长度测量等。– 资料 直线检测算法汇总_技术挖掘者的博客-CSDN博客_直线检测算法 直线检测算法博文中缺失的几个源码(Hough_lin…

联想乐商店更新安卓APK错误处理

当你点击“重新提交”&#xff0c;联想开放平台会卡死 其实他们的网页是有BUG的。HTTP GET appDetail请求会有个服务器内部错误 联系了联想客服&#xff0c;他们的绕过去方案是&#xff0c;你要选择“已上架” 然后再更新版本就可以了

【Python机器学习系列】建立XGBoost模型预测心脏疾病(完整实现过程)

一、引言 前文回顾&#xff1a; 一文彻底搞懂机器学习中的归一化与反归一化问题 【Python机器学习系列】一文彻底搞懂机器学习中表格数据的输入形式&#xff08;理论源码&#xff09; 【Python机器学习系列】一文带你了解机器学习中的Pipeline管道机制&#xff08;理论源码…

【Python】03快速上手爬虫案例三:搞定药师帮

文章目录 前言1、破解验证码2、获取数据 前言 提示&#xff1a;通过用户名、密码、搞定验证码&#xff0c;登录进药师帮网站&#xff0c;然后抓取想要的数据。 爬取数据&#xff0c;最终效果图&#xff1a; 1、破解验证码 使用药师帮测试系统&#xff1a;https://dianrc.ysb…

2024 高级前端面试题之 HTML 「精选篇」

该内容主要整理关于 HTML 的相关面试题&#xff0c;其他内容面试题请移步至 「最新最全的前端面试题集锦」 查看。 HTML模块精选篇 1. 如何理解HTML语义化2. H5的新特性有哪些3. 说一下 HTML5 Drag API4. iframe有那些缺点5. 如何实现浏览器内多个标签页之间的通信6. 简述一下s…

Elasticsearch:如何为 Elastic Stack 配置 AI Assistant

了解并安装 Elastic AI Assistant Elastic 推出了 Observability AI Assistant&#xff0c;这是一款利用生成式 AI 来增强你的 Observability 体验的强大工具。 该 AI 助手由 OpenAI 或 Azure OpenAI 服务的连接器提供支持&#xff0c;可带来上下文洞察和聊天功能&#xff0c;…

104.乐理基础-五线谱-中音谱号、次中音谱号

内容参考于&#xff1a;三分钟音乐社 上一个内容&#xff1a;103.乐理基础-五线谱-低音谱号-CSDN博客 上一个内容练习答案&#xff1a; 常用的谱号就是下图所示的四个&#xff0c;其中高音谱号与低音谱号已经在上一个内容和上上一个内容中写过了&#xff0c;音乐中百分之九十…

【C++历练之路】探秘C++三大利器之一——多态

W...Y的主页 &#x1f60a; 代码仓库分享&#x1f495; 前言&#x1f354;: 在计算机科学的广袤领域中&#xff0c;C多态性是一门令人着迷的技术艺术&#xff0c;它赋予我们的代码更强大的灵活性和可维护性。想象一下&#xff0c;你正在构建一个程序&#xff0c;需要适应不断…